Пример #1
0
    def test_perturbation_nonzero_angle(self):

        theta = np.pi / 3
        distance = 5
        deltaN = 2

        state_two_subspace = [pi.StateTwo(["Rb", "Rb"], [42, 42], [0, 0], [1 / 2, 1 / 2], [-1 / 2, -1 / 2]),
                              pi.StateTwo(["Rb", "Rb"], [42, 42], [0, 0], [1 / 2, 1 / 2], [-1 / 2, 1 / 2]),
                              pi.StateTwo(["Rb", "Rb"], [42, 42], [0, 0], [1 / 2, 1 / 2], [1 / 2, -1 / 2]),
                              pi.StateTwo(["Rb", "Rb"], [42, 42], [0, 0], [1 / 2, 1 / 2], [1 / 2, 1 / 2])]

        ### Schrieffer Wolff transformation ###

        state_one = state_two_subspace[0].getFirstState()
        system_one = pi.SystemOne(state_one.getSpecies(), self.cache)
        system_one.restrictEnergy(state_one.getEnergy() - 200, state_one.getEnergy() + 200)
        system_one.restrictN(state_one.getN() - deltaN, state_one.getN() + deltaN)
        system_one.restrictL(state_one.getL() - 1, state_one.getL() + 1)
        system_two = pi.SystemTwo(system_one, system_one, self.cache)
        system_two.restrictEnergy(state_two_subspace[0].getEnergy() - 20, state_two_subspace[0].getEnergy() + 20)

        # System containing the unperturbed Hamiltonian
        system_two_unperturbed = pi.SystemTwo(system_two)

        # System containing the perturbed Hamiltonian
        system_two_perturbed = pi.SystemTwo(system_two)
        system_two_perturbed.setDistance(distance)
        system_two_perturbed.setAngle(theta)

        # Restrict unperturbed system to the subspace
        system_two_unperturbed.constrainBasisvectors(system_two_unperturbed.getBasisvectorIndex(state_two_subspace))

        # Apply the Schrieffer Wolff transformation on the perturbed Hamiltonian
        system_two_perturbed.applySchriefferWolffTransformation(system_two_unperturbed)

        # Effective Hamiltonian
        hamiltonian_schrieffer_wolff = system_two_perturbed.getHamiltonian()

        ### Perturbation theory up to second order ###

        calculator = pi.PerturbativeInteraction(theta, self.cache)

        E0 = calculator.getEnergy(state_two_subspace)
        C3 = calculator.getC3(state_two_subspace)
        C6 = calculator.getC6(state_two_subspace, deltaN)

        # Effective Hamiltonian
        hamiltonian_second_order = E0 + C3 / distance**3 + C6 / distance**6

        ### Compare results ###

        np.testing.assert_allclose(hamiltonian_schrieffer_wolff.A, hamiltonian_second_order, rtol=1e-2)
 def calcEnergies_two(distance):
     tmp = pi.SystemTwo(system_two) # copy allows to restrict energy further
     tmp.setDistance(distance)
     tmp.diagonalize()
     tmp.restrictEnergy(state_two.energy-0.1, state_two.energy+0.1)
     tmp.buildHamiltonian() # has to be called to apply the new restriction in energy
     return tmp
    def test_parallelization(self):
        #######################################################
        ### Check parallelization of one atom calculations ####
        #######################################################

        # Define state
        state_one = pi.StateOne("Rb", 61, 2, 1.5, 1.5)

        # Build one atom system
        system_one = pi.SystemOne(state_one.element, self.path_cache)
        system_one.restrictEnergy(state_one.energy-40, state_one.energy+40)
        system_one.restrictN(state_one.n-1, state_one.n+1)
        system_one.restrictL(state_one.l-1, state_one.l+1)
        system_one.setEfield([0, 0, 1])
        system_one.buildInteraction() # will save time in the following

        # Calculate stark shifts in parallel
        efields = np.linspace(0,5,5)

        global calcEnergies_one
        def calcEnergies_one(field):
            tmp = pi.SystemOne(system_one) # copy allows to restrict energy further
            tmp.setEfield([0, 0, field])
            tmp.diagonalize()
            tmp.restrictEnergy(state_one.energy-20, state_one.energy+20)
            tmp.buildHamiltonian() # has to be called to apply the new restriction in energy
            return tmp

        p = Pool(2)
        stark_shifted_systems = np.array(p.map(calcEnergies_one, efields))
        p.close()

        # Get potential lines
        line_mapper = np.array([])
        line_idx = []
        line_step = []
        line_val = []

        for i in range(len(stark_shifted_systems)):

            # Get line segments pointing from iFirst to iSecond
            if i > 0:
                connections = stark_shifted_systems[i-1].getConnections(stark_shifted_systems[i], 0.001)
                iFirst = np.array(connections[0])
                iSecond = np.array(connections[1])

            else:
                iFirst = np.arange(stark_shifted_systems[i].getNumVectors())
                iSecond = iFirst

            if len(iFirst) > 0:
                # Enlarge the mapper of line indices so that every iFirst is mapped to a line index
                line_mapper.resize(np.max(iFirst)+1)
                boolarr = line_mapper[iFirst] == 0
                tmp = line_mapper[iFirst]
                tmp[boolarr] = np.max(line_mapper)+1+np.arange(np.sum(boolarr))
                line_mapper[iFirst] = tmp

                # Store line segments    
                line_idx += line_mapper[iFirst].tolist()
                line_step += (i*np.ones(len(iFirst))).tolist()
                line_val += stark_shifted_systems[i].diagonal[iSecond].tolist()

                # Assign the line indices to iSecond, delete line indices that could not be assigned
                tmp = line_mapper[iFirst]
                line_mapper = np.zeros(np.max(iSecond)+1)
                line_mapper[iSecond] = tmp

        lines_energies = coo_matrix((line_val, (np.array(line_idx)-1,line_step))).toarray()
        lines_energies[lines_energies == 0] = np.nan

        self.assertEqual(len(lines_energies), 30)
        self.assertEqual(np.sum(np.isnan(lines_energies)), 2)

        #######################################################
        ### Check parallelization of two atom calculations ####
        #######################################################

        # Define state
        state_two = pi.StateTwo(state_one, state_one)

        # Build two atom system
        system_two = pi.SystemTwo(system_one, system_one, self.path_cache)
        system_two.restrictEnergy(state_two.energy-2, state_two.energy+2)
        system_two.setConservedParityUnderPermutation(pi.ODD)
        system_two.setDistance(1)
        system_two.buildInteraction() # will save time in the following

        # Calculate dipole interaction in parallel
        distances = np.linspace(10,4,5)

        global calcEnergies_two
        def calcEnergies_two(distance):
            tmp = pi.SystemTwo(system_two) # copy allows to restrict energy further
            tmp.setDistance(distance)
            tmp.diagonalize()
            tmp.restrictEnergy(state_two.energy-0.1, state_two.energy+0.1)
            tmp.buildHamiltonian() # has to be called to apply the new restriction in energy
            return tmp

        p = Pool(2)
        dipole_interacting_systems = np.array(p.map(calcEnergies_two, distances))
        p.close()

        # Get potential lines
        line_mapper = np.array([])
        line_idx = []
        line_step = []
        line_val = []

        for i in range(len(dipole_interacting_systems)):

            # Get line segments pointing from iFirst to iSecond
            if i > 0:
                connections = dipole_interacting_systems[i-1].getConnections(dipole_interacting_systems[i], 0.001)
                iFirst = np.array(connections[0])
                iSecond = np.array(connections[1])

            else:
                iFirst = np.arange(dipole_interacting_systems[i].getNumVectors())
                iSecond = iFirst

            if len(iFirst) > 0:
                # Enlarge the mapper of line indices so that every iFirst is mapped to a line index
                line_mapper.resize(np.max(iFirst)+1)
                boolarr = line_mapper[iFirst] == 0
                tmp = line_mapper[iFirst]
                tmp[boolarr] = np.max(line_mapper)+1+np.arange(np.sum(boolarr))
                line_mapper[iFirst] = tmp

                # Store line segments    
                line_idx += line_mapper[iFirst].tolist()
                line_step += (i*np.ones(len(iFirst))).tolist()
                line_val += dipole_interacting_systems[i].diagonal[iSecond].tolist()

                # Assign the line indices to iSecond, delete line indices that could not be assigned
                tmp = line_mapper[iFirst]
                line_mapper = np.zeros(np.max(iSecond)+1)
                line_mapper[iSecond] = tmp

        lines_energies = coo_matrix((line_val, (np.array(line_idx)-1,line_step))).toarray()
        lines_energies[lines_energies == 0] = np.nan

        self.assertEqual(len(lines_energies), 10)
        self.assertEqual(np.sum(np.isnan(lines_energies)), 0)
Пример #4
0
    def test_perturbation_efield(self):

        distance = 5
        efieldz = 0.5
        deltaN = 2

        state_two_subspace = [pi.StateTwo(["Rb", "Rb"], [42, 42], [0, 1], [1 / 2, 1 / 2], [1 / 2, -1 / 2]),
                              pi.StateTwo(["Rb", "Rb"], [42, 42], [0, 1], [1 / 2, 1 / 2], [-1 / 2, 1 / 2]),
                              pi.StateTwo(["Rb", "Rb"], [42, 42], [1, 0], [1 / 2, 1 / 2], [1 / 2, -1 / 2]),
                              pi.StateTwo(["Rb", "Rb"], [42, 42], [1, 0], [1 / 2, 1 / 2], [-1 / 2, 1 / 2])]

        ### Exact calculation ###

        state_one = state_two_subspace[0].getFirstState()
        system_one = pi.SystemOne(state_one.getSpecies(), self.cache)
        system_one.restrictEnergy(state_one.getEnergy() - 200, state_one.getEnergy() + 200)
        system_one.restrictN(state_one.getN() - deltaN, state_one.getN() + deltaN)
        system_one.restrictL(state_one.getL() - 1, state_one.getL() + 1)
        system_one.setEfield([0, 0, efieldz])

        system_one.diagonalize(1e-3)

        fieldshift = system_one.getHamiltonian().diagonal()[
            system_one.getBasisvectorIndex(state_two_subspace[0].getFirstState())] + \
            system_one.getHamiltonian().diagonal()[
            system_one.getBasisvectorIndex(state_two_subspace[0].getSecondState())]

        system_two = pi.SystemTwo(system_one, system_one, self.cache)
        system_two.setMinimalNorm(0.01)
        system_two.restrictEnergy(fieldshift - 20, fieldshift + 20)
        system_two.setConservedMomentaUnderRotation([int(np.sum(state_two_subspace[0].getM()))])
        system_two.setDistance(distance)

        # Get eigenenergies
        system_two.diagonalize()
        energies_exact = system_two.getHamiltonian().diagonal(
        )[list(system_two.getBasisvectorIndex(state_two_subspace))]

        ### Schrieffer Wolff transformation ###

        state_one = state_two_subspace[0].getFirstState()
        system_one = pi.SystemOne(state_one.getSpecies(), self.cache)
        system_one.restrictEnergy(state_one.getEnergy() - 200, state_one.getEnergy() + 200)
        system_one.restrictN(state_one.getN() - deltaN, state_one.getN() + deltaN)
        system_one.restrictL(state_one.getL() - 1, state_one.getL() + 1)
        system_one.setEfield([0, 0, efieldz])

        system_one.diagonalize(1e-3)

        fieldshift = system_one.getHamiltonian().diagonal()[
            system_one.getBasisvectorIndex(state_two_subspace[0].getFirstState())] + \
            system_one.getHamiltonian().diagonal()[
            system_one.getBasisvectorIndex(state_two_subspace[0].getSecondState())]

        system_two = pi.SystemTwo(system_one, system_one, self.cache)
        system_two.setMinimalNorm(0.01)
        system_two.restrictEnergy(fieldshift - 20, fieldshift + 20)
        system_two.setConservedMomentaUnderRotation([int(np.sum(state_two_subspace[0].getM()))])

        # System containing the unperturbed Hamiltonian
        system_two_unperturbed = pi.SystemTwo(system_two)

        # System containing the perturbed Hamiltonian
        system_two_perturbed = pi.SystemTwo(system_two)
        system_two_perturbed.setDistance(distance)

        # Restrict unperturbed system to the subspace
        system_two_unperturbed.constrainBasisvectors(system_two_unperturbed.getBasisvectorIndex(state_two_subspace))

        # IMPORTANT: Chose the states such that the basisvector matrix is a unit matrix
        # The step is necessary because we removed from SystemTwo some irrelevant states and basis vectors which where only important to get the Stark shift
        # right, so that the basisvector matrix is not unitary anymore. This is due to diagonalization with a non-zero threshold (SystemOne.diagonalize),
        # setting a conserved momentum (SystemTwo.setConservedMomentaUnderRotation), and removing states that rarely occur (SystemTwo.setMinimalNorm).
        # We solve this problem by interpreting the basis vectors a new states. In the basis of these states, the basis vector matrix is a unit matrix and
        # thus unitary.
        system_two_unperturbed.unitarize()
        system_two_perturbed.unitarize()

        # Apply the Schrieffer Wolff transformation on the perturbed Hamiltonian
        system_two_perturbed.applySchriefferWolffTransformation(system_two_unperturbed)

        # Get eigenenergies
        hamiltonian_schrieffer_wolff = system_two_perturbed.getHamiltonian()
        energies_schrieffer_wolff = np.linalg.eigvalsh(hamiltonian_schrieffer_wolff.todense())

        ### Compare results ###
        np.testing.assert_allclose(np.sort(energies_exact), np.sort(energies_schrieffer_wolff), rtol=1e-12)