TB2Jflows 0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
TB2Jflows/ase_siesta.py CHANGED
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
 
6
6
  import ase
7
7
  from ase.io.jsonio import decode, encode
8
- from pyDFTutils.siesta import MySiesta
8
+ #from pyDFTutils.siesta import MySiesta
9
9
  from TB2J.interfaces import gen_exchange_siesta
10
10
  from TB2J.io_merge import merge
11
11
  from TB2J.rotate_atoms import rotate_atom_spin, rotate_atom_xyz
@@ -24,7 +24,6 @@ class SiestaFlow:
24
24
  restart=True,
25
25
  metadata={},
26
26
  fdf_arguments={},
27
- relax_arguments={},
28
27
  split_soc=False,
29
28
  **kwargs,
30
29
  ):
@@ -51,7 +50,6 @@ class SiestaFlow:
51
50
  self.root_path = root_path
52
51
  self.restart = restart
53
52
  self.kwargs = kwargs
54
- self.relax_args = relax_arguments
55
53
 
56
54
  # paths
57
55
  self.metadata_path = os.path.join(self.root_path, "metadata.json")
@@ -358,9 +356,9 @@ class SiestaFlow:
358
356
  write_path=os.path.join(self.root_path, "TB2J_results_merged"),
359
357
  )
360
358
 
361
- def runall_collinear(self, atoms, relax=True, scf=True, TB2J=True, **kwargs):
359
+ def runall_collinear(self, atoms, relax=True, scf=True, TB2J=True, **kwargs):
362
360
  if relax:
363
- atoms = self.relax(atoms, **self.relax_args)
361
+ atoms = self.relax(atoms)
364
362
  if scf:
365
363
  self.scf_calculation_collinear(atoms, label="siesta")
366
364
  if TB2J:
@@ -370,7 +368,7 @@ class SiestaFlow:
370
368
  self, atoms, relax=True, scf=True, TB2J=True, rotate_type="structure", **kwargs
371
369
  ):
372
370
  if relax:
373
- atoms = self.relax(atoms, **self.relax_args)
371
+ atoms = self.relax(atoms)
374
372
  if scf:
375
373
  self.scf_calculation_with_rotations(
376
374
  atoms, rotate_type=rotate_type, label="siesta"
@@ -381,7 +379,7 @@ class SiestaFlow:
381
379
 
382
380
  def runall_split_soc(self, atoms, relax=True, scf=True, TB2J=True, **kwargs):
383
381
  if relax:
384
- atoms = self.relax(atoms, **self.relax_args)
382
+ atoms = self.relax(atoms)
385
383
  if scf:
386
384
  self.scf_calculatoin_split_soc(atoms, label="siesta")
387
385
  if TB2J:
@@ -52,7 +52,6 @@ def auto_siesta_TB2J(
52
52
  TB2J=True,
53
53
  rotate_type="structure",
54
54
  fincore=True,
55
- relax_arguments={},
56
55
  siesta_kwargs={},
57
56
  TB2J_kwargs={},
58
57
  fdf_kwargs={},
@@ -100,7 +99,6 @@ def auto_siesta_TB2J(
100
99
  fincore=fincore,
101
100
  Udict=Udict,
102
101
  split_soc=split_soc,
103
- relax_arguments = relax_arguments,
104
102
  **siesta_kwargs,
105
103
  )
106
104
  flow.write_metadata()
TB2Jflows/find_pp.py ADDED
@@ -0,0 +1,67 @@
1
+ import os
2
+ from ase.data import chemical_symbols, atomic_numbers
3
+
4
+
5
+ class PPFinder():
6
+ def __init__(self):
7
+ pass
8
+
9
+ def get_pp_path(self, element, xc, label, rel):
10
+ pass
11
+
12
+ class DojoFinder():
13
+ def __init__(self, path=None):
14
+ if path is None:
15
+ self.path = os.environ['DOJO_PATH']
16
+ else:
17
+ self.path = path
18
+
19
+ def get_pp_path(self,
20
+ xc : str,
21
+ typ='NC',
22
+ rel='sr',
23
+ version='04',
24
+ accuracy='standard',
25
+ fmt='psml'):
26
+ typ=typ.lower()
27
+ xc=xc.lower()
28
+ if xc=="lda":
29
+ xc="pw"
30
+ dirname = os.path.join(self.path, f"{typ}-{rel}-{version}_{xc}_{accuracy}_{fmt}")
31
+ if not os.path.exists(dirname):
32
+ raise FileNotFoundError(f"File Not found: {dirname}")
33
+ return dirname
34
+
35
+ def get_pp_fname(self,
36
+ element,
37
+ xc : str,
38
+ typ='NC',
39
+ rel='sr',
40
+ version='04',
41
+ accuracy='standard',
42
+ fincore=False,
43
+ fmt='psml'):
44
+ if 57<atomic_numbers[element]<=70:
45
+ if fincore:
46
+ fname = os.path.join(self.get_pp_path(xc=xc, typ=typ, rel=rel,
47
+ version=version, accuracy=accuracy, fmt=fmt), f"fincore/{element}.{fmt}")
48
+ else:
49
+ fname = os.path.join(self.get_pp_path(xc=xc, typ=typ, rel=rel,
50
+ version=version, accuracy=accuracy, fmt=fmt), f"withf/{element}.{fmt}")
51
+
52
+ else:
53
+ fname = os.path.join(self.get_pp_path(xc=xc, typ=typ, rel=rel,
54
+ version=version, accuracy=accuracy, fmt=fmt), f"{element}.{fmt}")
55
+ if not os.path.exists(fname):
56
+ raise FileNotFoundError(f"File Not found: {fname}")
57
+ return fname
58
+
59
+
60
+
61
+ def test():
62
+ finder=DojoFinder(path=os.path.expanduser('~/projects/pp/dojo'))
63
+ fname=finder.get_pp_path(element='Sr', xc='pbesol')
64
+ fname=finder.get_pp_path(element='Srg', xc='pbe')
65
+
66
+ if __name__=='__main__':
67
+ test()
TB2Jflows/mysiesta.py ADDED
@@ -0,0 +1,521 @@
1
+ import os
2
+ import math
3
+ import numpy as np
4
+ from ase import Atoms
5
+ from ase.calculators.siesta import Siesta
6
+ from ase.calculators.calculator import FileIOCalculator, ReadError
7
+ from ase.calculators.siesta.parameters import format_fdf
8
+ from ase.calculators.siesta.parameters import Species, PAOBasisBlock
9
+ from ase.io import write
10
+ import copy
11
+
12
+ import shutil
13
+ from os.path import join, isfile, islink
14
+ import numpy as np
15
+ from ase.units import Ry, eV, Bohr
16
+ from ase.data import atomic_numbers
17
+ #from ase.io.siesta import read_siesta_xv
18
+ #from ase.calculators.siesta.import_functions import \
19
+ # get_valence_charge, read_vca_synth_block
20
+ from ase.calculators.calculator import FileIOCalculator, ReadError
21
+ from ase.calculators.calculator import Parameters, all_changes
22
+ from ase.calculators.siesta.parameters import PAOBasisBlock, Species
23
+ from ase.calculators.siesta.parameters import format_fdf
24
+ #from pyDFTutils.siesta.pdos import gen_pdos_figure, plot_layer_pdos
25
+
26
+ from .siesta_basis import get_basis, fincore_basis, withf_basis
27
+ from .findpp import DojoFinder
28
+
29
+
30
+ synthetic_atoms_dict_fincore={
31
+ "Yb":((6,5,5,5), (2,6,1,0))
32
+ }
33
+
34
+ def read_siesta_xv(fd):
35
+ vectors = []
36
+ for i in range(3):
37
+ data = next(fd).split()
38
+ vectors.append([float(data[j]) * Bohr for j in range(3)])
39
+
40
+ # Read number of atoms (line 4)
41
+ natoms = int(next(fd).split()[0])
42
+
43
+ # Read remaining lines
44
+ speciesnumber, atomnumbers, xyz, V = [], [], [], []
45
+ for line in fd:
46
+ if len(line) > 5: # Ignore blank lines
47
+ data = line.split()
48
+ speciesnumber.append(int(data[0]))
49
+ atomnumbers.append(int(data[1])%200)
50
+ xyz.append([float(data[2 + j]) * Bohr for j in range(3)])
51
+ V.append([float(data[5 + j]) * Bohr for j in range(3)])
52
+
53
+ vectors = np.array(vectors)
54
+ atomnumbers = np.array(atomnumbers)
55
+ xyz = np.array(xyz)
56
+ atoms = Atoms(numbers=atomnumbers, positions=xyz, cell=vectors,
57
+ pbc=True)
58
+ assert natoms == len(atoms)
59
+ return atoms
60
+
61
+ def read_xv(fname):
62
+ with open(fname) as myfile:
63
+ atoms=read_siesta_xv(myfile)
64
+ return atoms
65
+
66
+
67
+ def get_species(atoms, xc, rel='sr', accuracy='standard'):
68
+ finder = DojoFinder()
69
+ elems = list(dict.fromkeys(atoms.get_chemical_symbols()).keys())
70
+ elem_dict = dict(zip(elems, range(1, len(elems) + 1)))
71
+ pseudo_path = finder.get_pp_path(xc=xc)
72
+ species = [
73
+ Species(symbol=elem,
74
+ pseudopotential=finder.get_pp_fname(
75
+ elem, xc=xc, rel=rel, accuracy=accuracy, fincore=fincore),
76
+ ghost=False) for elem in elem_dict.keys()
77
+ ]
78
+ return pseudo_path, species
79
+
80
+
81
+ def cart2sph(vec):
82
+ x, y, z = vec
83
+ r = np.linalg.norm(vec) # r
84
+ if r < 1e-10:
85
+ theta, phi = 0.0, 0.0
86
+ else:
87
+ # note that there are many conventions, here is the ISO convention.
88
+ phi = math.atan2(y, x) * 180/math.pi # phi
89
+ theta = math.acos(z/r) * 180/math.pi # theta
90
+ return r, theta, phi
91
+
92
+
93
+ class MySiesta(Siesta):
94
+ def __init__(self,
95
+ atoms=None,
96
+ command=None,
97
+ xc='LDA',
98
+ spin='non-polarized',
99
+ basis_set='DZP',
100
+ species=None,
101
+ ghosts=[],
102
+ synthetic_atoms={},
103
+ input_basis_set={},
104
+ pseudo_path=None,
105
+ input_pp={},
106
+ pp_accuracy='standard',
107
+ fincore=False,
108
+ **kwargs):
109
+ # non-perturnbative polarized orbital.
110
+ self.npt_elems = set()
111
+ self.synthetic_atoms=synthetic_atoms
112
+
113
+ if fincore:
114
+ input_basis_set.update(fincore_basis)
115
+ else:
116
+ input_basis_set.update(withf_basis)
117
+
118
+ if atoms is not None:
119
+ finder = DojoFinder()
120
+ elems = list(dict.fromkeys(atoms.get_chemical_symbols()).keys())
121
+ self.elem_dict = dict(zip(elems, range(1, len(elems) + 1)))
122
+ symbols = atoms.get_chemical_symbols()
123
+
124
+ # ghosts
125
+ ghost_symbols = [symbols[i] for i in ghosts]
126
+ ghost_elems = list(dict.fromkeys(ghost_symbols).keys())
127
+ tags = [1 if i in ghosts else 0 for i in range(len(atoms))]
128
+ atoms.set_tags(tags)
129
+
130
+ if pseudo_path is None:
131
+ pseudo_path = finder.get_pp_path(xc=xc, accuracy=pp_accuracy)
132
+
133
+ if spin == 'spin-orbit':
134
+ rel = 'fr'
135
+ else:
136
+ rel = 'sr'
137
+ species = []
138
+ for elem, index in self.elem_dict.items():
139
+ if elem not in input_basis_set:
140
+ bselem = basis_set
141
+ if elem in ['Li', 'Be', 'Na', 'Mg', 'Sm']:
142
+ self.npt_elems.add(f"{elem}.{index}")
143
+ else:
144
+ bselem = PAOBasisBlock(input_basis_set[elem])
145
+ if elem not in input_pp:
146
+ pseudopotential = finder.get_pp_fname(
147
+ elem, xc=xc, rel=rel, accuracy=pp_accuracy, fincore=fincore)
148
+ else:
149
+ pseudopotential = os.path.join(
150
+ pseudo_path, input_pp[elem])
151
+
152
+ if elem in self.synthetic_atoms:
153
+ excess_charge = 0
154
+ else:
155
+ excess_charge = None
156
+
157
+ species.append(Species(symbol=elem,
158
+ pseudopotential=pseudopotential,
159
+ basis_set=bselem,
160
+ ghost=False,
161
+ excess_charge=excess_charge))
162
+ for elem in ghost_elems:
163
+ species.append(
164
+ Species(symbol=elem,
165
+ pseudopotential=finder.get_pp_fname(
166
+ elem, xc=xc, rel=rel, accuracy=pp_accuracy,
167
+ fincore=fincore),
168
+ tag=1,
169
+ ghost=True))
170
+
171
+
172
+ Siesta.__init__(self,
173
+ xc=xc,
174
+ spin=spin,
175
+ atoms=atoms,
176
+ pseudo_path=pseudo_path,
177
+ species=species,
178
+ **kwargs)
179
+ self.set_npt_elements()
180
+ self.set_synthetic_atoms()
181
+
182
+ def _write_species(self, fd, atoms):
183
+ """Write input related the different species.
184
+
185
+ Parameters:
186
+ - f: An open file object.
187
+ - atoms: An atoms object.
188
+ """
189
+ species, species_numbers = self.species(atoms)
190
+
191
+ if self['pseudo_path'] is not None:
192
+ pseudo_path = self['pseudo_path']
193
+ elif 'SIESTA_PP_PATH' in os.environ:
194
+ pseudo_path = os.environ['SIESTA_PP_PATH']
195
+ else:
196
+ mess = "Please set the environment variable 'SIESTA_PP_PATH'"
197
+ raise Exception(mess)
198
+
199
+ fd.write(format_fdf('NumberOfSpecies', len(species)))
200
+ fd.write(format_fdf('NumberOfAtoms', len(atoms)))
201
+
202
+ pao_basis = []
203
+ chemical_labels = []
204
+ basis_sizes = []
205
+ synth_blocks = []
206
+ for species_number, spec in enumerate(species):
207
+ species_number += 1
208
+ symbol = spec['symbol']
209
+ atomic_number = atomic_numbers[symbol]
210
+
211
+ if spec['pseudopotential'] is None:
212
+ if self.pseudo_qualifier() == '':
213
+ label = symbol
214
+ pseudopotential = label + '.psf'
215
+ else:
216
+ label = '.'.join([symbol, self.pseudo_qualifier()])
217
+ pseudopotential = label + '.psf'
218
+ else:
219
+ pseudopotential = spec['pseudopotential']
220
+ label = os.path.basename(pseudopotential)
221
+ label = '.'.join(label.split('.')[:-1])
222
+
223
+ if not os.path.isabs(pseudopotential):
224
+ pseudopotential = join(pseudo_path, pseudopotential)
225
+
226
+ if not os.path.exists(pseudopotential):
227
+ mess = "Pseudopotential '%s' not found" % pseudopotential
228
+ raise RuntimeError(mess)
229
+
230
+ name = os.path.basename(pseudopotential)
231
+ name = name.split('.')
232
+ name.insert(-1, str(species_number))
233
+ if spec['ghost']:
234
+ name.insert(-1, 'ghost')
235
+ atomic_number = -atomic_number
236
+
237
+ name = '.'.join(name)
238
+ pseudo_targetpath = self.getpath(name)
239
+
240
+ if join(os.getcwd(), name) != pseudopotential:
241
+ if islink(pseudo_targetpath) or isfile(pseudo_targetpath):
242
+ os.remove(pseudo_targetpath)
243
+ symlink_pseudos = self['symlink_pseudos']
244
+
245
+ symlink_pseudos = False
246
+
247
+ if symlink_pseudos is None:
248
+ symlink_pseudos = not os.name == 'nt'
249
+
250
+ if symlink_pseudos:
251
+ os.symlink(pseudopotential, pseudo_targetpath)
252
+ else:
253
+ shutil.copy(pseudopotential, pseudo_targetpath)
254
+ if not spec['excess_charge'] is None:
255
+ atomic_number += 200
256
+ n_atoms = sum(np.array(species_numbers) == species_number)
257
+
258
+ if spec['excess_charge'] != 0:
259
+ paec = float(spec['excess_charge']) / n_atoms
260
+ vc = get_valence_charge(pseudopotential)
261
+ fraction = float(vc + paec) / vc
262
+ pseudo_head = name[:-4]
263
+ fractional_command = os.environ['SIESTA_UTIL_FRACTIONAL']
264
+ cmd = '%s %s %.7f' % (fractional_command,
265
+ pseudo_head,
266
+ fraction)
267
+ os.system(cmd)
268
+
269
+ pseudo_head += '-Fraction-%.5f' % fraction
270
+ synth_pseudo = pseudo_head + '.psf'
271
+ synth_block_filename = pseudo_head + '.synth'
272
+ os.remove(name)
273
+ shutil.copyfile(synth_pseudo, name)
274
+ synth_block = read_vca_synth_block(
275
+ synth_block_filename,
276
+ species_number=species_number)
277
+ synth_blocks.append(synth_block)
278
+ else:
279
+ synth_block = self.synthetic_atoms[symbol]
280
+
281
+
282
+
283
+ if len(synth_blocks) > 0:
284
+ fd.write(format_fdf('SyntheticAtoms', list(synth_blocks)))
285
+
286
+ label = '.'.join(np.array(name.split('.'))[:-1])
287
+ string = ' %d %d %s' % (species_number, atomic_number, label)
288
+ chemical_labels.append(string)
289
+ if isinstance(spec['basis_set'], PAOBasisBlock):
290
+ pao_basis.append(spec['basis_set'].script(label))
291
+ else:
292
+ basis_sizes.append((" " + label, spec['basis_set']))
293
+ fd.write((format_fdf('ChemicalSpecieslabel', chemical_labels)))
294
+ fd.write('\n')
295
+ fd.write((format_fdf('PAO.Basis', pao_basis)))
296
+ fd.write((format_fdf('PAO.BasisSizes', basis_sizes)))
297
+ fd.write('\n')
298
+
299
+
300
+ def set_npt_elements(self):
301
+ if len(self.npt_elems) > 0:
302
+ npt_text = []
303
+ for name in self.npt_elems:
304
+ npt_text.append(
305
+ f"{name} non-perturbative ")
306
+ # npt_text += "%endblock PAO.PolarizationScheme\n"
307
+ self['fdf_arguments'].update({"PAO.PolarizationScheme": npt_text})
308
+
309
+ def set_synthetic_atoms(self):
310
+ print("setting syn")
311
+ nsyn=len(self.synthetic_atoms)
312
+ if nsyn> 0:
313
+ syntext = []
314
+ #syntext.append(f"{nsyn}")
315
+ for name, content in self.synthetic_atoms.items():
316
+ syntext.append(
317
+ f"{self.elem_dict[name]}")
318
+ syntext.append(
319
+ " ".join([str(x) for x in content[0]]))
320
+ syntext.append(
321
+ " ".join([str(x) for x in content[1]]))
322
+ self['fdf_arguments'].update({"SyntheticAtoms": syntext})
323
+
324
+ #print("setting syn:", syntext)
325
+
326
+ def set_fdf_arguments(self, fdf_arguments):
327
+ self['fdf_arguments'].update(fdf_arguments)
328
+
329
+ def set_mixer(self,
330
+ method='pulay',
331
+ weight=0.05,
332
+ history=10,
333
+ restart=25,
334
+ restart_save=4,
335
+ linear_after=0,
336
+ linear_after_weight=0.1):
337
+ pass
338
+
339
+ def update_fdf_arguments(self, fdf_arguments):
340
+ fdf = self['fdf_arguments'].update(fdf_arguments)
341
+
342
+ def add_Hubbard_U(self,
343
+ specy,
344
+ n=3,
345
+ l=2,
346
+ U=0,
347
+ J=0,
348
+ rc=0.0,
349
+ Fermi_cut=0.0,
350
+ scale_factor='0.95'):
351
+ if not 'Udict' in self.__dict__:
352
+ self.Udict = dict()
353
+ idx=self.elem_dict[specy]
354
+ specy_label=f"{specy}.{idx}"
355
+ self.Udict[specy_label] = {
356
+ 'n': n,
357
+ 'l': l,
358
+ 'U': U,
359
+ 'J': J,
360
+ 'rc': rc,
361
+ 'Fermi_cut': Fermi_cut,
362
+ 'scale_factor': scale_factor
363
+ }
364
+ self.set_Hubbard_U(self.Udict)
365
+
366
+ def set_Hubbard_U(self, Udict):
367
+ """
368
+ Udict: {'Fe': {'n':n, 'l':l, 'U':U, 'J', J, 'rc':rc, 'Fermi_cut':Fermi_cut }}
369
+ """
370
+ Ublock = []
371
+ for key, val in Udict.items():
372
+ Ublock.append(' %s %s ' % (key, 1))
373
+ if val['n'] is not None:
374
+ Ublock.append(' n=%s %s' % (val['n'], val['l']))
375
+ else:
376
+ Ublock.append('%s' % (val['l']))
377
+ Ublock.append(' %s %s' % (val['U'], val['J']))
378
+ if 'rc' in val:
379
+ Ublock.append(' %s %s' % (val['rc'], val['Fermi_cut']))
380
+ Ublock.append(' %s' % val['scale_factor'])
381
+
382
+ self.update_fdf_arguments(
383
+ {'LDAU.Proj': Ublock, 'LDAU.ProjectorGenerationMethod': 2})
384
+
385
+ def set_Udict(self, Udict):
386
+ """
387
+ Udict: e.g. {"Fe":{"n":3, "l":2, "U":3.0, "J":0.0}, ...}
388
+ or {"Fe":{[3, 2, 3, 0]}
389
+
390
+ """
391
+ for specy, val in Udict.items():
392
+ if isinstance(val, dict):
393
+ self.add_Hubbard_U(specy, **val)
394
+ else:
395
+ self.add_Hubbard_U(specy, *val)
396
+
397
+ def write_Hubbard_block(self, f):
398
+ pass
399
+
400
+ def relax(
401
+ self,
402
+ atoms,
403
+ TypeOfRun='Broyden',
404
+ VariableCell=True,
405
+ ConstantVolume=False,
406
+ RelaxCellOnly=False,
407
+ MaxForceTol=0.001,
408
+ MaxStressTol=1,
409
+ NumCGSteps=40,
410
+ relaxed_file="relaxed.vasp"
411
+ ):
412
+ pbc = atoms.get_pbc()
413
+ initial_magnetic_moments = atoms.get_initial_magnetic_moments()
414
+ self.update_fdf_arguments({
415
+ 'MD.TypeOfRun': TypeOfRun,
416
+ 'MD.VariableCell': VariableCell,
417
+ 'MD.ConstantVolume': ConstantVolume,
418
+ 'MD.RelaxCellOnly': RelaxCellOnly,
419
+ 'MD.MaxForceTol': "%s eV/Ang" % MaxForceTol,
420
+ 'MD.MaxStressTol': "%s GPa" % MaxStressTol,
421
+ 'MD.NumCGSteps': NumCGSteps,
422
+ })
423
+ self.calculate(atoms)
424
+ #self.read(self.prefix + '.XV')
425
+ self.atoms=read_xv(os.path.join(self.directory, self.prefix + '.XV'))
426
+ self.atoms.set_pbc(pbc)
427
+ self.atoms.set_initial_magnetic_moments(initial_magnetic_moments)
428
+ atoms = self.atoms
429
+ self.update_fdf_arguments({
430
+ 'MD.NumCGSteps': 0,
431
+ })
432
+ if relaxed_file is not None:
433
+ write(relaxed_file, atoms, vasp5=True, sort=False)
434
+ return self.atoms
435
+
436
+ def scf_calculation(self, atoms, dos=True, kpts=[7,7,7], **kwargs):
437
+ if dos:
438
+ k1, k2, k3 = kpts
439
+ self.update_fdf_arguments({'WriteEigenvalues': '.true.',
440
+ 'ProjectedDensityOfStates': ['-70.00 30.0 0.015 3000 eV'],
441
+ 'PDOS.kgrid_Monkhorst_Pack': [f'{k1} 0 0 0.0',
442
+ f'0 {k2} 0 0.0',
443
+ f'0 0 {k3} 0.0']})
444
+ self.calculate(atoms, **kwargs)
445
+
446
+ def _write_structure(self, f, atoms):
447
+ """Translate the Atoms object to fdf-format.
448
+
449
+ Parameters:
450
+ - f: An open file object.
451
+ - atoms: An atoms object.
452
+ """
453
+ cell = atoms.cell
454
+ f.write('\n')
455
+
456
+ if cell.rank in [1, 2]:
457
+ raise ValueError('Expected 3D unit cell or no unit cell. You may '
458
+ 'wish to add vacuum along some directions.')
459
+
460
+ # Write lattice vectors
461
+ if np.any(cell):
462
+ f.write(format_fdf('LatticeConstant', '1.0 Ang'))
463
+ f.write('%block LatticeVectors\n')
464
+ for i in range(3):
465
+ for j in range(3):
466
+ s = (' %.15f' % cell[i, j]).rjust(16) + ' '
467
+ f.write(s)
468
+ f.write('\n')
469
+ f.write('%endblock LatticeVectors\n')
470
+ f.write('\n')
471
+
472
+ self._write_atomic_coordinates(f, atoms)
473
+
474
+ # Write magnetic moments.
475
+ magmoms = atoms.get_initial_magnetic_moments()
476
+
477
+ # The DM.InitSpin block must be written to initialize to
478
+ # no spin. SIESTA default is FM initialization, if the
479
+ # block is not written, but we must conform to the
480
+ # atoms object.
481
+ if magmoms is not None:
482
+ if len(magmoms) == 0:
483
+ f.write('#Empty block forces ASE initialization.\n')
484
+
485
+ f.write('%block DM.InitSpin\n')
486
+ if len(magmoms) != 0 and isinstance(magmoms[0], np.ndarray):
487
+ for n, Mcart in enumerate(magmoms):
488
+ M = cart2sph(Mcart)
489
+ if M[0] != 0:
490
+ f.write(' %d %.14f %.14f %.14f \n' %
491
+ (n + 1, M[0], M[1], M[2]))
492
+ elif len(magmoms) != 0 and isinstance(magmoms[0], float):
493
+ for n, M in enumerate(magmoms):
494
+ if M != 0:
495
+ f.write(' %d %.14f \n' % (n + 1, M))
496
+ f.write('%endblock DM.InitSpin\n')
497
+ f.write('\n')
498
+
499
+ def my_read_results(self):
500
+ """Read the results.
501
+ """
502
+ #self.read_number_of_grid_points()
503
+ self.read_energy()
504
+ self.read_forces_stress()
505
+ # self.read_eigenvalues()
506
+ self.read_kpoints()
507
+ self.read_dipole()
508
+ self.read_pseudo_density()
509
+ # self.read_hsx()
510
+ self.read_dim()
511
+ # if self.results['hsx'] is not None:
512
+ # self.read_pld(self.results['hsx'].norbitals,
513
+ # len(self.atoms))
514
+ # self.atoms.cell = self.results['pld'].cell * Bohr
515
+ # else:
516
+ # self.results['pld'] = None
517
+
518
+ # self.read_wfsx()
519
+ self.read_ion(self.atoms)
520
+
521
+ self.read_bands()
@@ -0,0 +1,50 @@
1
+
2
+ withf_basis = {
3
+ #"La": "5\nn=5 0 1\n3.889 \n1.0 \nn=6 0 2\n9.113 6.906 \n1.0 1.0 \nn=5 1 1\n4.548 \n1.0 \nn=6 1 1\n9.113 \n1.0 \nn=5 2 2\n7.55 5.398 \n1.0 1.0 \n",
4
+ "Ce": "6\nn=5 0 1\n3.805 \n1.0 \nn=6 0 2\n9.041 6.837 \n1.0 1.0 \nn=5 1 1\n4.458 \n1.0 \nn=6 1 1\n9.041 \n1.0 \nn=5 2 2\n7.489 5.339 \n1.0 1.0 \nn=4 3 2\n4.114 2.494 \n1.0 1.0 \n",
5
+ "Pr": "6\nn=5 0 1\n3.737 \n1.0 \nn=6 0 2\n8.879 6.702 \n1.0 1.0 \nn=5 1 1\n4.387 \n1.0 \nn=6 1 1\n8.879 \n1.0 \nn=5 2 2\n7.46 5.307 \n1.0 1.0 \nn=4 3 2\n3.944 2.369 \n1.0 1.0 \n",
6
+ "Nd": "6\nn=5 0 1\n3.67 \n1.0 \nn=6 0 2\n8.808 6.641 \n1.0 1.0 \nn=5 1 1\n4.317 \n1.0 \nn=6 1 1\n8.808 \n1.0 \nn=5 2 2\n7.445 5.285 \n1.0 1.0 \nn=4 3 2\n3.805 2.271 \n1.0 1.0 \n",
7
+ "Pm": "6\nn=5 0 1\n3.611 \n1.0 \nn=6 0 2\n8.756 6.588 \n1.0 1.0 \nn=5 1 1\n4.257 \n1.0 \nn=6 1 1\n8.756 \n1.0 \nn=5 2 2\n7.43 5.269 \n1.0 1.0 \nn=4 3 2\n3.692 2.189 \n1.0 1.0 \n",
8
+ "Sm": "6\nn=5 0 1\n3.554 \n1.0 \nn=6 0 2\n8.668 6.516 \n1.0 1.0 \nn=5 1 1\n4.197 \n1.0 \nn=6 1 1\n8.668 \n1.0 \nn=5 2 2\n7.445 5.269 \n1.0 1.0 \nn=4 3 2\n3.59 2.115 \n1.0 1.0 \n",
9
+ "Eu": "6\nn=5 0 1\n3.497 \n1.0 \nn=6 0 2\n8.599 6.458 \n1.0 1.0 \nn=5 1 1\n4.139 \n1.0 \nn=6 1 1\n8.599 \n1.0 \nn=5 2 2\n7.46 5.275 \n1.0 1.0 \nn=4 3 2\n3.504 2.052 \n1.0 1.0 \n",
10
+ "Gd": "6\nn=5 0 1\n3.442 \n1.0 \nn=6 0 2\n8.514 6.374 \n1.0 1.0 \nn=5 1 1\n4.089 \n1.0 \nn=6 1 1\n8.514 \n1.0 \nn=5 2 2\n7.489 5.285 \n1.0 1.0 \nn=4 3 2\n3.435 1.997 \n1.0 1.0 \n",
11
+ "Tb": "6\nn=5 0 1\n3.394 \n1.0 \nn=6 0 2\n8.395 6.279 \n1.0 1.0 \nn=5 1 1\n4.04 \n1.0 \nn=6 1 1\n8.395 \n1.0 \nn=5 2 2\n7.52 5.301 \n1.0 1.0 \nn=4 3 2\n3.366 1.948 \n1.0 1.0 \n",
12
+ "Dy": "6\nn=5 0 1\n3.346 \n1.0 \nn=6 0 2\n8.295 6.186 \n1.0 1.0 \nn=5 1 1\n3.992 \n1.0 \nn=6 1 1\n8.295 \n1.0 \nn=5 2 2\n7.565 5.323 \n1.0 1.0 \nn=4 3 2\n3.306 1.901 \n1.0 1.0 \n",
13
+ "Ho": "6\nn=5 0 1\n3.293 \n1.0 \nn=6 0 2\n8.179 6.093 \n1.0 1.0 \nn=5 1 1\n3.944 \n1.0 \nn=6 1 1\n8.179 \n1.0 \nn=5 2 2\n7.61 5.349 \n1.0 1.0 \nn=4 3 2\n3.254 1.86 \n1.0 1.0 \n",
14
+ "Er": "6\nn=5 0 1\n3.247 \n1.0 \nn=6 0 2\n8.049 5.972 \n1.0 1.0 \nn=5 1 1\n3.897 \n1.0 \nn=6 1 1\n8.049 \n1.0 \nn=5 2 2\n7.656 5.387 \n1.0 1.0 \nn=4 3 2\n3.208 1.821 \n1.0 1.0 \n",
15
+ "Tm": "6\nn=5 0 1\n3.202 \n1.0 \nn=6 0 2\n7.921 5.872 \n1.0 1.0 \nn=5 1 1\n3.858 \n1.0 \nn=6 1 1\n7.921 \n1.0 \nn=5 2 2\n7.718 5.419 \n1.0 1.0 \nn=4 3 2\n3.163 1.785 \n1.0 1.0 \n",
16
+ "Yb": "6\nn=5 0 1\n3.163 \n1.0 \nn=6 0 2\n7.796 5.761 \n1.0 1.0 \nn=5 1 1\n3.812 \n1.0 \nn=6 1 1\n7.796 \n1.0 \nn=5 2 2\n7.78 5.468 \n1.0 1.0 \nn=4 3 2\n3.119 1.753 \n1.0 1.0 \n"
17
+ }
18
+
19
+ fincore_basis ={
20
+ #"La": "4\nn=5 0 1\n3.889 \n1.0 \nn=6 0 2\n9.113 6.906 \n1.0 1.0 \nn=5 1 1\n4.548 \n1.0 \nn=6 1 1\n9.113 \n1.0 \nn=5 2 2\n7.55 5.398 \n1.0 1.0 \n",
21
+ "Ce": "5\nn=5 0 1\n3.805 \n1.0 \nn=6 0 2\n8.968 6.776 \n1.0 1.0 \nn=5 1 1\n4.458 \n1.0 \nn=6 1 1\n8.968 \n1.0 \nn=5 2 2\n7.489 5.339 \n1.0 1.0 \n",
22
+ "Pr": "5\nn=5 0 1\n3.737 \n1.0 \nn=6 0 2\n8.791 6.621 \n1.0 1.0 \nn=5 1 1\n4.387 \n1.0 \nn=6 1 1\n8.791 \n1.0 \nn=5 2 2\n7.46 5.307 \n1.0 1.0 \n",
23
+ "Nd": "5\nn=5 0 1\n3.67 \n1.0 \nn=6 0 2\n8.634 6.49 \n1.0 1.0 \nn=5 1 1\n4.317 \n1.0 \nn=6 1 1\n8.634 \n1.0 \nn=5 2 2\n7.445 5.285 \n1.0 1.0 \n",
24
+ "Pm": "5\nn=5 0 1\n3.611 \n1.0 \nn=6 0 2\n8.446 6.33 \n1.0 1.0 \nn=5 1 1\n4.257 \n1.0 \nn=6 1 1\n8.446 \n1.0 \nn=5 2 2\n7.43 5.269 \n1.0 1.0 \n",
25
+ "Sm": "5\nn=5 0 1\n3.554 \n1.0 \nn=6 0 2\n8.328 6.223 \n1.0 1.0 \nn=5 1 1\n4.197 \n1.0 \nn=6 1 1\n8.328 \n1.0 \nn=5 2 2\n7.445 5.269 \n1.0 1.0 \n",
26
+ "Eu": "5\nn=5 0 1\n3.497 \n1.0 \nn=6 0 2\n8.278 6.186 \n1.0 1.0 \nn=5 1 1\n4.139 \n1.0 \nn=6 1 1\n8.278 \n1.0 \nn=5 2 2\n7.46 5.275 \n1.0 1.0 \n",
27
+ "Gd": "5\nn=5 0 1\n3.442 \n1.0 \nn=6 0 2\n8.278 6.179 \n1.0 1.0 \nn=5 1 1\n4.089 \n1.0 \nn=6 1 1\n8.278 \n1.0 \nn=5 2 2\n7.489 5.285 \n1.0 1.0 \n",
28
+ "Tb": "5\nn=5 0 1\n3.394 \n1.0 \nn=6 0 2\n8.147 6.063 \n1.0 1.0 \nn=5 1 1\n4.04 \n1.0 \nn=6 1 1\n8.147 \n1.0 \nn=5 2 2\n7.52 5.301 \n1.0 1.0 \n",
29
+ "Dy": "5\nn=5 0 1\n3.346 \n1.0 \nn=6 0 2\n7.969 5.919 \n1.0 1.0 \nn=5 1 1\n3.992 \n1.0 \nn=6 1 1\n7.969 \n1.0 \nn=5 2 2\n7.565 5.323 \n1.0 1.0 \n",
30
+ "Ho": "5\nn=5 0 1\n3.293 \n1.0 \nn=6 0 2\n7.827 5.79 \n1.0 1.0 \nn=5 1 1\n3.944 \n1.0 \nn=6 1 1\n7.827 \n1.0 \nn=5 2 2\n7.61 5.349 \n1.0 1.0 \n",
31
+ "Er": "5\nn=5 0 1\n3.247 \n1.0 \nn=6 0 2\n7.626 5.624 \n1.0 1.0 \nn=5 1 1\n3.897 \n1.0 \nn=6 1 1\n7.626 \n1.0 \nn=5 2 2\n7.656 5.387 \n1.0 1.0 \n",
32
+ "Tm": "5\nn=5 0 1\n3.202 \n1.0 \nn=6 0 2\n7.474 5.49 \n1.0 1.0 \nn=5 1 1\n3.858 \n1.0 \nn=6 1 1\n7.474 \n1.0 \nn=5 2 2\n7.718 5.419 \n1.0 1.0 \n",
33
+ "Yb": "5\nn=5 0 1\n3.163 \n1.0 \nn=6 0 2\n7.312 5.36 \n1.0 1.0 \nn=5 1 1\n3.812 \n1.0 \nn=6 1 1\n7.312 \n1.0 \nn=5 2 2\n7.78 5.468 \n1.0 1.0 \n"
34
+ }
35
+
36
+ def get_basis(element, fincore=False):
37
+ if not fincore:
38
+ return withf_basis[element]
39
+ elif basis == "fincore":
40
+ return fincore_basis[element]
41
+ else:
42
+ raise ValueError("Invalid basis set: {}".format(basis))
43
+
44
+ if __name__ == "__main__":
45
+ print(get_basis("La"))
46
+ print(get_basis("La", fincore=True))
47
+ print(get_basis("Ce"))
48
+ print(get_basis("Ce", fincore=True))
49
+
50
+
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: TB2Jflows
3
+ Version: 0.1.0
4
+ Summary: TB2Jflows: Workflows for automatically calculation of exchange parameters using TB2J
5
+ Author-email: Xu He <mailhexu@gmail.com>
6
+ License: BSD-2-clause
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
14
+ Classifier: Topic :: Scientific/Engineering :: Physics
15
+ Requires-Python: >=3.6
16
+ Requires-Dist: ase
17
+ Requires-Dist: sisl
18
+ Requires-Dist: tb2j
19
+ Description-Content-Type: text/markdown
20
+
21
+ # TB2Jflows
22
+ Workflows for automatically calculation of exchange parameters from DFT
23
+
24
+ ## Installation
25
+
26
+ First download the package from the github page. Run the following command in the TB2Jflows directory
27
+
28
+ ```
29
+ pip install . --user
30
+ ```
31
+
32
+ will install TB2Jflows and the dependencies.
33
+
34
+ You need the following things to
35
+
36
+ - Siesta built with psml and netcdf.
37
+ - Pseudopotentials from PseudoDojo Dataset.
38
+ - Configure the command to run siesta and the path to the pseudopotentials, e.g.
39
+
40
+ ```
41
+ export ASE_IESTA_COMMAND="mpirun siesta < PREFIX.fdf > PREFIX.out 2> PREFIX.err"
42
+ export DOJO_PATH='$HOME/.local/pp/dojo'
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ Below is an example of calculating the exchange parameters of SrMnO3:
48
+
49
+ ```python
50
+ from ase.io import read
51
+ from TB2Jflows import SiestaFlow
52
+
53
+
54
+ def calculate_siesta_TB2J_SrMnO3():
55
+ atoms = read('SrMnO3.STRUCT_OUT')
56
+ atoms.set_initial_magnetic_moments([0, 3, 0, 0, 0])
57
+ flow = SiestaFlow(atoms,
58
+ spin='spin-orbit',
59
+ restart=True,
60
+ root_path='SrMnO3')
61
+ flow.write_metadata()
62
+ atoms = flow.relax(atoms)
63
+ flow.scf_calculation_with_rotations(atoms)
64
+ flow.run_TB2J(magnetic_elements='Mn',
65
+ nz=50,
66
+ kmesh=[7, 7, 7],
67
+ Rcut=18,
68
+ np=10,
69
+ use_cache=True)
70
+ flow.run_TB2J_merge()
71
+
72
+
73
+ if __name__ == '__main__':
74
+ calculate_siesta_TB2J_SrMnO3()
75
+ ~
76
+ ```
77
+
@@ -0,0 +1,11 @@
1
+ TB2Jflows/__init__.py,sha256=0aRVj9v-u64LxJl7Cyxcr4YUjpeMkBGPuv-nJbxbHOg,100
2
+ TB2Jflows/ase_siesta.py,sha256=1b6j0dFNnXCeLnCTWoLAJ158JQul8UcORIttGOhNydw,14366
3
+ TB2Jflows/auto_siesta_TB2J.py,sha256=c5dviznEbXyO3BJbh-LNBlC9efT7YbdC7ydWxyCJZ5E,3113
4
+ TB2Jflows/find_pp.py,sha256=iTqxLPuR88Jsuh4lc7hPEzFczhoOB0x6K5MWqYTEeiQ,2134
5
+ TB2Jflows/mysiesta.py,sha256=DV9dnYibrpPzegKy_MvoMOOrmoom2NPKblhVNBUZkeg,18943
6
+ TB2Jflows/run_abacus.py,sha256=96tZ2n02FFLl8IZf4W_4TvkCcsd6oS1FVEIqhZpY76o,2692
7
+ TB2Jflows/siesta_basis.py,sha256=UIO0VF_558jIK6kGctqEwHyyV_t7KEWGOtv4Obw4fyg,5408
8
+ tb2jflows-0.1.0.dist-info/METADATA,sha256=mnUu8VXO3Dndka3rbgqWJ0hiWxOXmWyF9AjauzpVQZk,2170
9
+ tb2jflows-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ tb2jflows-0.1.0.dist-info/licenses/LICENSE,sha256=KNu68sa-XR_2jZJKhDcSnxoNve8jtHgkw_w9PjP1YOk,1315
11
+ tb2jflows-0.1.0.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: TB2Jflows
3
- Version: 0.1
4
- Summary: TB2Jflows: Workflows for automatically calculation of exchange parameters using TB2J
5
- Author: Xu He
6
- Author-email: mailhexu@gmail.com
7
- License: BSD-2-clause
8
- Classifier: Development Status :: 3 - Alpha
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Intended Audience :: Science/Research
12
- Classifier: Topic :: Scientific/Engineering :: Chemistry
13
- Classifier: Topic :: Scientific/Engineering :: Physics
14
- Classifier: License :: OSI Approved :: BSD License
15
- Requires-Python: >=3.6
16
- License-File: LICENSE
17
- Requires-Dist: TB2J
18
- Requires-Dist: ase
19
- Requires-Dist: sisl
20
- Requires-Dist: pyDFTutils
21
- Dynamic: author
22
- Dynamic: author-email
23
- Dynamic: classifier
24
- Dynamic: description
25
- Dynamic: license
26
- Dynamic: license-file
27
- Dynamic: requires-dist
28
- Dynamic: requires-python
29
- Dynamic: summary
30
-
31
- TB2Jflows: Workflows for automatically calculation of exchange parameters using TB2J
@@ -1,9 +0,0 @@
1
- TB2Jflows/__init__.py,sha256=0aRVj9v-u64LxJl7Cyxcr4YUjpeMkBGPuv-nJbxbHOg,100
2
- TB2Jflows/ase_siesta.py,sha256=zvKfKPidFvndfMQH0PHDprnFpwirk4yKdqwWDBe-_EI,14493
3
- TB2Jflows/auto_siesta_TB2J.py,sha256=hrnJos-Rkx1bngFOSr4wATUr0NVyBXmSOsrvscfMLmg,3180
4
- TB2Jflows/run_abacus.py,sha256=96tZ2n02FFLl8IZf4W_4TvkCcsd6oS1FVEIqhZpY76o,2692
5
- tb2jflows-0.1.dist-info/licenses/LICENSE,sha256=KNu68sa-XR_2jZJKhDcSnxoNve8jtHgkw_w9PjP1YOk,1315
6
- tb2jflows-0.1.dist-info/METADATA,sha256=BFLB9Ngpq9iLlEAxF245S9snQgmTFTp3FBypy5azHkY,967
7
- tb2jflows-0.1.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
8
- tb2jflows-0.1.dist-info/top_level.txt,sha256=iYRLHB7ZeHb59fEZLnbqJDymBKWPqfVgmvqd9S51Txw,10
9
- tb2jflows-0.1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- TB2Jflows