def traingular_2d_hamiltonian(J1=-0e-21, k1=np.array([-000 * mu_B]), k1dir=np.array([[0.0, 0, 1.0]])): """ Isolated spin in an applied field. field:10T, time step: 1fs. Total time: 100 ps, No Langevin term. """ # make model atoms = Atoms( symbols="H", positions=[[0, 0, 0]], cell=[[1, 0, 0], [-1.0 / 2, np.sqrt(3) / 2, 0], [0, 0, 1]]) spin = np.array([[0, 1, 0]]) ham = SpinHamiltonian( cell=atoms.cell, pos=atoms.get_scaled_positions(), spinat=spin, zion=atoms.get_atomic_numbers()) ham.gilbert_damping = [1.0] J = { (0, 0, (1, 0, 0)): J1, (0, 0, (0, 1, 0)): J1, (0, 0, (1, 1, 0)): J1, (0, 0, (-1, 0, 0)): J1, (0, 0, (0, -1, 0)): J1, (0, 0, (-1, -1, 0)): J1, } ham.set_exchange_ijR(exchange_Jdict=J) ham.set_uniaxial_mca(k1, k1dir) return ham
def square_2d_hamiltonian(Jx=-5e-21, Jy=-5e-21, k1=np.array([-000 * mu_B]), k1dir=np.array([[0.0, 0, 1.0]])): atoms = Atoms( symbols="H", positions=[[0, 0, 0]], cell=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) spin = np.array([[0, 1, 0]]) ham = SpinHamiltonian( cell=atoms.cell, pos=atoms.get_scaled_positions(), spinat=spin, zion=atoms.get_atomic_numbers()) ham.gilbert_damping = [1.0] J = { (0, 0, (1, 0, 0)): Jx, (0, 0, (-1, 0, 0)): Jx, (0, 0, (0, 1, 0)): Jy, (0, 0, (0, -1, 0)): Jy, } ham.set_exchange_ijR(exchange_Jdict=J) ham.set_uniaxial_mca(k1, k1dir) return ham
def canting_1d_hamiltonian( J1=3e-21, #J2=0e-21, DMI1=[0, 0, 0e-21], DMI2=[0, 0, -0e-21], k1=np.array([-0 * mu_B]), k1dir=np.array([[0.0, 0.0, 1.0]]), plot_type='2d'): # make model atoms = Atoms(symbols="H", positions=[[0, 0, 0]], cell=[1, 1, 1]) spin = np.array([[0, 1, 0]]) ham = SpinHamiltonian( cell=atoms.cell, pos=atoms.get_scaled_positions(), spinat=spin, zion=atoms.get_atomic_numbers()) ham.gilbert_damping = [0.8] #ham.gyro_ratio=[1.0] J = { (0, 0, (1, 0, 0)): J1, (0, 0, (-1, 0, 0)): J1, #(0, 0, (2, 0, 0)): J2, #(0, 0, (-2, 0, 0)): J2, } ham.set_exchange_ijR(exchange_Jdict=J) k1 = k1 k1dir = k1dir ham.set_uniaxial_mca(k1, k1dir) sc_ham = ham.make_supercell(np.diag([2, 1, 1])) DMI1val = np.array(DMI1) DMI2val = np.array(DMI2) DMI = { (0, 1, (0, 0, 0)): DMI1val, (1, 0, (0, 0, 0)): -DMI1val, (0, 1, (-1, 0, 0)): -DMI2val, (1, 0, (1, 0, 0)): DMI2val, } sc_ham.set_dmi_ijR(dmi_ddict=DMI) return sc_ham
def cubic_3d_2site_hamiltonian(Jx=-0e-21, Jy=-0e-21, Jz=0e-21, DMI=[0, 0, 0e-21], k1=np.array([-0 * mu_B]), k1dir=np.array([[0.0, 0, 1.0]])): atoms = Atoms( symbols="H", positions=[[0, 0, 0]], cell=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) spin = np.array([[0, 1, 0]]) ham = SpinHamiltonian( cell=atoms.cell, pos=atoms.get_scaled_positions(), spinat=spin, zion=atoms.get_atomic_numbers()) ham.gilbert_damping = [0.8] J = { (0, 0, (1, 0, 0)): Jx, (0, 0, (-1, 0, 0)): Jx, (0, 0, (0, 1, 0)): Jy, (0, 0, (0, -1, 0)): Jy, (0, 0, (0, 0, 1)): Jz, (0, 0, (0, 0, -1)): Jz, } ham.set_exchange_ijR(exchange_Jdict=J) ham.set_uniaxial_mca(k1, k1dir) sc_ham = ham.make_supercell(np.diag([2, 1, 1])) DMIval = np.array(DMI) DMI = { (0, 1, (0, 0, 0)): DMIval, #(1, 0, (0, 0, 0)): -DMIval, (0, 1, (-1, 0, 0)): DMIval, #(1, 0, (1, 0, 0)): -DMIval, #(0, 0, (0, 1, 0)): DMIval, #(0, 0, (0, -1, 0)): -DMIval, #(0, 0, (-1, 0, 0)): -DMIval, #(0, 0, (1, 0, 0)): DMIval, } sc_ham.set_dmi_ijR(dmi_ddict=DMI) return sc_ham
def __init__(self, geometry=None, **kwargs) -> None: if geometry == None: atoms = Atoms(**kwargs) elif type(geometry) == ase.atoms.Atoms: atoms = geometry.copy() elif Path(geometry).is_file(): if str(Path(geometry).parts[-1]) == "geometry.in.next_step": atoms = ase.io.read(geometry, format="aims") else: try: atoms = ase.io.read(geometry) except Exception as excpt: logger.error(str(excpt)) raise Exception( "ASE was not able to recognize the file format, e.g., a non-standard cif-format." ) elif Path(geometry).is_dir(): raise Exception( "You specified a directory as input. The geometry must be a file." ) else: atoms = None assert type(atoms) == ase.atoms.Atoms, "Atoms not read correctly." # Get data from another Atoms object: numbers = atoms.get_atomic_numbers() positions = atoms.get_positions() cell = atoms.get_cell() celldisp = atoms.get_celldisp() pbc = atoms.get_pbc() constraint = [c.copy() for c in atoms.constraints] masses = atoms.get_masses() magmoms = None charges = None momenta = None if atoms.has("initial_magmoms"): magmoms = atoms.get_initial_magnetic_moments() if atoms.has("initial_charges"): charges = atoms.get_initial_charges() if atoms.has("momenta"): momenta = atoms.get_momenta() self.arrays = {} super().__init__( numbers=numbers, positions=positions, cell=cell, celldisp=celldisp, pbc=pbc, constraint=constraint, masses=masses, magmoms=magmoms, charges=charges, momenta=momenta, ) self._is_1d = None self._is_2d = None self._is_3d = None self._periodic_axes = None self._check_lattice_vectors() try: self.sg = ase.spacegroup.get_spacegroup(self, symprec=1e-2) except: self.sg = ase.spacegroup.Spacegroup(1) self.lattice = self.cell.get_bravais_lattice().crystal_family
class NWChem(FileIOCalculator): #FU| added charges to possible properties calculation #FU| need more variety on the charge calculation implemented_properties = ['energy', 'forces', 'dipole', 'magmom', 'charges'] command = 'nwchem PREFIX.nw > PREFIX.out' #for testing purposes #command = 'mpirun -np 4 nwchem PREFIX.nw > PREFIX.out' default_parameters = dict( xc='LDA', smearing=None, charge=None, task='energy', #task='gradient', # Warning: nwchem centers atoms by default # see ase-developers/2012-March/001356.html geometry='nocenter noautosym', convergence={'energy': None, 'density': None, 'gradient': None, 'lshift': None, # set lshift to 0.0 for nolevelshifting }, basis='3-21G', basispar=None, ecp=None, so=None, spinorbit=False, odft=False, bq=None, #FU| no default mulliken charges for DFT, so we can't do anything here by default charges=None, wfn='RHF', raw='') # additional outside of dft block control string def __init__(self, restart=None, ignore_bad_restart_file=False, label='nwchem', atoms=None, **kwargs): """Construct NWchem-calculator object.""" FileIOCalculator.__init__(self, restart, ignore_bad_restart_file, label, atoms, **kwargs) def set(self, **kwargs): changed_parameters = FileIOCalculator.set(self, **kwargs) if changed_parameters: self.reset() def check_state(self, atoms): system_changes = FileIOCalculator.check_state(self, atoms) # Ignore unit cell and boundary conditions: if 'cell' in system_changes: system_changes.remove('cell') if 'pbc' in system_changes: system_changes.remove('pbc') return system_changes def write_input(self, atoms, properties=None, system_changes=None): FileIOCalculator.write_input(self, atoms, properties, system_changes) p = self.parameters p.initial_magmoms = atoms.get_initial_magnetic_moments().tolist() p.write(self.label + '.ase') del p['initial_magmoms'] f = open(self.label + '.nw', 'w') if p.charge is not None: f.write('charge %s\n' % p.charge) #check here, we could give here only the list of atoms that are not bq's #and write an additional file containing the bq's write_nwchem(f, atoms, p.geometry) f.write('start\n') if p.basispar is not None: basispar = 'basis ' + p.basispar else: if p.basis.find('cc-p') > -1: basispar = 'basis "ao basis" spherical ' else: basispar = 'basis' def format_basis_set(string, tag=basispar): formatted = tag + '\n' lines = string.split('\n') if len(lines) > 1: formatted += string else: formatted += ' * library ' + string return formatted + '\nend\n' basis = format_basis_set(p.basis) if p.ecp is not None: basis += format_basis_set(p.ecp, 'ecp') if p.so is not None: basis += format_basis_set(p.so, 'so') f.write(basis) if p.xc == 'HF': task = 'scf' f.write('\nscf\n') if 'mult' in p: if p.mult > 1: if p.wfn != 'ROHF' and p.wfn != 'UHF': print 'now setting your wavefunction to unrestricted as you did not specify anything else' self.parameters.wfn = 'UHF' p.wfn = 'UHF' f.write('\nnopen %i\n' %(p.mult-1)) f.write('\n%s\nend\n' %p.wfn) else: f.write('\nend\n') elif p.xc == 'MP2': task = 'mp2' #FUDO| the whole thing depends on the basis set, i think (Dunning depends, Pople I don't know) #FUDO| so we need to check that and also that it does not get set if parameters.corecol is True f.write('\nmp2\nfreeze atomic\nend\n') else: if p.spinorbit: task = 'sodft' else: task = 'dft' xc = {'LDA': 'slater pw91lda', 'PBE': 'xpbe96 cpbe96', 'revPBE': 'revpbe cpbe96', 'RPBE': 'rpbe cpbe96'}.get(p.xc, p.xc) f.write('\n' + task + '\n') f.write(' xc ' + xc + '\n') for key in p.convergence: if p.convergence[key] is not None: if key == 'lshift': if p.convergence[key] <= 0.0: f.write(' convergence nolevelshifting\n') else: f.write(' convergence %s %s\n' % (key, p.convergence[key] / Hartree)) else: f.write(' convergence %s %s\n' % (key, p.convergence[key])) if p.smearing is not None: assert p.smearing[0].lower() == 'gaussian', p.smearing f.write(' smear %s\n' % (p.smearing[1] / Hartree)) if 'mult' not in p: # Obtain multiplicity from magnetic momenta: #FUDO| how to handle if we have an open-shell system? Does not seem to work here... tot_magmom = atoms.get_initial_magnetic_moments().sum() if tot_magmom < 0: mult = tot_magmom - 1 # fill minority bands else: mult = tot_magmom + 1 else: mult = p.mult if mult != int(mult): raise RuntimeError('Noninteger multiplicity not possible. ' + 'Check initial magnetic moments.') if 'mult' in p: if p.mult > 1: if p.wfn != 'ROHF' and p.wfn != 'UHF': print 'now setting your wavefunction to unrestricted as you did not specify anything else' self.parameters.wfn = 'UHF' p.wfn = 'UHF' f.write(' mult %i\n' %(mult)) if p.wfn == 'ROHF': f.write(' rodft\n cgmin\n') if p.odft: f.write(' odft\n') # open shell aka spin polarized dft for key in sorted(p.keys()): if key in ['charge', 'geometry', 'basis', 'basispar', 'ecp', 'so', 'xc', 'spinorbit', 'convergence', 'smearing', #FU| needed to add a key exception for bq, and charges... 'raw', 'mult', 'task', 'odft', 'bq', 'charges', 'wfn']: #FU| continue f.write(u" {0} {1}\n".format(key, p[key])) f.write('end\n') #FU| with NWChem 6.3 both variants are equivalent, something was weird with a previous version if p.bq is not None: #f.write('\nbq\n load %s format 1 2 3 4\nend\n' %('.bq')) #FUDO| this label splitting kinda sucks, but it seems to work right now if the whole thing #FUDO| is run from (a) directory(ies) above f.write('\nbq\n load %s format 1 2 3 4\nend\n' %(self.label.split('/')[-1] + '.bq')) bqf = open(self.label + '.bq', 'w') for q in p.bq: bqf.write('%21.10f %21.10f %21.10f %21.10f\n' %(q[0], q[1], q[2], q[3])) bqf.close() #FU| careful in earlier NWChems this might be the wrong order: #f.write('\nbq\n') #for q in p.bq: # f.write('%21.10f %21.10f %21.10f %21.10f\n' %(q[0], q[1], q[2], q[3])) #f.write('\nend\n') #FU| maybe it's better to put this into the raw input if p.charges is not None: if p.charges.find('ESP') > -1: f.write('\nesp\n') if p.charges.find('RESP') > -1: f.write('restrain\n') f.write('end\n') if p.charges.find('Mulliken') > -1: f.write('\nProperty\nMulliken\nend\n') #FU| if p.raw: f.write(p.raw + '\n') f.write('\ntask ' + task + ' ' + p.task + '\n') if p.charges is not None: if p.charges.find('ESP') > -1: f.write('\ntask esp\n') if p.charges.find('Mulliken') > -1: f.write('\ntask ' + task + ' property\n') f.close() def read(self, label): FileIOCalculator.read(self, label) if not os.path.isfile(self.label + '.out'): raise ReadError f = open(self.label + '.nw') for line in f: if line.startswith('geometry'): break symbols = [] positions = [] for line in f: if line.startswith('end'): break words = line.split() symbols.append(words[0]) positions.append([float(word) for word in words[1:]]) self.parameters = Parameters.read(self.label + '.ase') self.atoms = Atoms(symbols, positions, magmoms=self.parameters.pop('initial_magmoms')) self.read_results() def read_results(self): self.read_energy() if self.parameters.task.find('gradient') > -1: self.read_forces() self.niter = self.read_number_of_iterations() self.nelect = self.read_number_of_electrons() self.nvector = self.read_number_of_bands() self.results['magmom'] = self.read_magnetic_moment() #the following will not work for regular, _unresctricted_ calculations #self.results['dipole'] = self.read_dipole_moment() if self.parameters.charges is not None: #print 'hello' self.results['charges'] = self.read_charges() #print self.results['charges'] def get_ibz_k_points(self): return np.array([0., 0., 0.]) def get_number_of_bands(self): return self.nvector def read_number_of_bands(self): nvector = 0 for line in open(self.label + '.out'): if line.find('Vector ') != -1: # count all printed vectors nvector += 1 if not nvector: nvector = None return nvector def get_number_of_electrons(self): return self.nelect def read_number_of_electrons(self): nelect = None for line in open(self.label + '.out'): # find last one if line.find('of electrons') != -1: nelect = float(line.split(':')[1].strip()) return nelect def get_number_of_iterations(self): return self.niter def read_number_of_iterations(self): niter = 0 for line in open(self.label + '.out'): if line.find('d= ') != -1: # count all iterations niter += 1 if not niter: niter = None return niter def read_magnetic_moment(self): magmom = None for line in open(self.label + '.out'): if line.find('Spin multiplicity') != -1: # last one magmom = float(line.split(':')[-1].strip()) if magmom < 0: magmom += 1 else: magmom -= 1 return magmom def read_dipole_moment(self): dipolemoment = [] for line in open(self.label + '.out'): for component in [ '1 1 0 0', '1 0 1 0', '1 0 0 1' ]: if line.find(component) != -1: value = float(line.split(component)[1].split()[0]) value = value * Bohr dipolemoment.append(value) if len(dipolemoment) == 0: assert len(self.atoms) == 1 dipolemoment = [0.0, 0.0, 0.0] return np.array(dipolemoment) def read_energy(self): """Read Energy from nwchem output file.""" text = open(self.label + '.out', 'r').read() lines = iter(text.split('\n')) # Energy: estring = 'Total ' if self.parameters.xc == 'HF': estring += 'SCF' elif self.parameters.xc == 'MP2': estring += 'MP2' else: estring += 'DFT' estring += ' energy' for line in lines: if line.find(estring) >= 0: energy = float(line.split()[-1]) break self.results['energy'] = energy * Hartree # Eigenstates spin = -1 kpts = [] for line in lines: if line.find('Molecular Orbital Analysis') >= 0: last_eps = -99999.0 spin += 1 kpts.append(KPoint(spin)) if spin >= 0: if line.find('Vector') >= 0: line = line.lower().replace('d', 'e') line = line.replace('=', ' ') word = line.split() this_occ = float(word[3]) this_eps = float(word[5]) kpts[spin].f_n.append(this_occ) kpts[spin].eps_n.append(this_eps) if this_occ < 0.1 and this_eps < last_eps: warn('H**O above LUMO - if this is not an exicted ' + 'state - this might be introduced by levelshift.', RuntimeWarning) last_eps = this_eps self.kpts = kpts def read_forces(self): """Read Forces from nwchem output file.""" file = open(self.label + '.out', 'r') lines = file.readlines() file.close() for i, line in enumerate(lines): if line.find('ENERGY GRADIENTS') >= 0: gradients = [] for j in range(i + 4, i + 4 + len(self.atoms)): word = lines[j].split() gradients.append([float(word[k]) for k in range(5, 8)]) self.results['forces'] = -np.array(gradients) * Hartree / Bohr def get_eigenvalues(self, kpt=0, spin=0): """Return eigenvalue array.""" return np.array(self.kpts[spin].eps_n) * Hartree def get_occupation_numbers(self, kpt=0, spin=0): """Return occupation number array.""" return self.kpts[spin].f_n def get_number_of_spins(self): """Return the number of spins in the calculation. Spin-paired calculations: 1, spin-polarized calculation: 2.""" return len(self.kpts) def get_spin_polarized(self): """Is it a spin-polarized calculation?""" return len(self.kpts) == 2 def read_charges(self, atoms=None): """read output from charge calculation""" #need to check status of calculator, if it was run, otherwise re-run it #dirty work-around is just to run it, because that should check by itself everything #self.get_potential_energy() if self.parameters.charges.find('ESP') > -1: if self.parameters.charges.find('RESP') > -1: charges = self.read_esp_charges(kind='RESP') else: charges = self.read_esp_charges() if self.parameters.charges.find('Mulliken') > -1: charges = self.read_mulliken_charges() if self.parameters.charges.find('Mulldef') > -1: charges = self.read_mulldef_charges() self.results['charges'] = np.array(charges)[:,-1] return np.array(charges)[:,-1] def read_esp_charges(self, kind='ESP'): #there is an easier way, because NWChem produces the output file self.label + '.q.' which contains all the necessary information file = open(self.label + '.q', 'r') lines = file.readlines() file.close() #FUDO| here there is too much silly wisdom, RESP charges will of course only be generated if we ask for them, nevertheless we shouldn't do it like this here: cols = range(1,4) #these are the coordinates of the charges if kind == 'RESP': cols.append(5) else: cols.append(4) charges = [] for j in range(1, 1 + len(self.atoms)): word = lines[j].split() charges.append([float(word[k]) for k in cols]) #file = open(self.label + '.out', 'r') #lines = file.readlines() #file.close() ##FUDO| here there is too much silly wisdom, RESP charges will of course only be generated if we ask for them, nevertheless we shouldn't do it like this here: #for i, line in enumerate(lines): # if line.find('ESP') >= 0: # cols = range(2,5) #these are the coordinates of the charges # if kind == 'RESP': # cols.append(6) # else: # cols.append(5) # charges = [] # for j in range(i + 3, i + 3 + len(self.atoms)): # word = lines[j].split() # charges.append([float(word[k]) for k in cols]) return charges def read_mulldef_charges(self): #there is an easier way, because NWChem produces the output file self.label + '.q.' which contains all the necessary information file = open(self.label + '.out', 'r') lines = file.readlines() file.close() core = self.atoms.get_atomic_numbers() for i, line in enumerate(lines): if line.find(' Mulliken analysis of the total density') >= 0: #print 'hello' col = 3 #this is the column of the charge magnitude, the positions are to be taken from the atoms positions charges = [] for anum, j in enumerate(range(i + 5, i + 5 + len(self.atoms))): word = lines[j].split() arr = self.atoms.positions[anum,:].tolist() #.append(float(word[col])) #print core[anum], float(word[col]) arr.append(float(core[anum]) - float(word[col])) #charges.append(self.atoms.positions[anum,:].tolist().append(float(word[col]))) charges.append(arr) #print charges return charges def read_mulliken_charges(self): #there is an easier way, because NWChem produces the output file self.label + '.q.' which contains all the necessary information file = open(self.label + '.out', 'r') lines = file.readlines() file.close() core = self.atoms.get_atomic_numbers() for i, line in enumerate(lines): if line.find('Total gross population on atoms') >= 0: #print 'hello' col = 3 #this is the column of the charge magnitude, the positions are to be taken from the atoms positions charges = [] for anum, j in enumerate(range(i + 2, i + 2 + len(self.atoms))): word = lines[j].split() arr = self.atoms.positions[anum,:].tolist() #.append(float(word[col])) #print core[anum], float(word[col]) arr.append(float(core[anum]) - float(word[col])) #charges.append(self.atoms.positions[anum,:].tolist().append(float(word[col]))) charges.append(arr) #print charges return charges def calculate(self, atoms=None, properties=['energy'], system_changes=all_changes): if 'forces' in properties: self.parameters.task = 'gradient' if 'charges' in properties and self.parameters.charges is None: self.parameters.charges = 'Mulliken' FileIOCalculator.calculate(self, atoms, properties, system_changes)