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)
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)