atomistics 0.2.1__tar.gz → 0.2.2__tar.gz

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.
Files changed (95) hide show
  1. {atomistics-0.2.1/atomistics.egg-info → atomistics-0.2.2}/PKG-INFO +5 -3
  2. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/_version.py +3 -3
  3. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/sphinxdft.py +77 -18
  4. atomistics-0.2.2/atomistics/calculators/vasp.py +219 -0
  5. {atomistics-0.2.1 → atomistics-0.2.2/atomistics.egg-info}/PKG-INFO +5 -3
  6. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics.egg-info/SOURCES.txt +3 -1
  7. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics.egg-info/requires.txt +5 -2
  8. {atomistics-0.2.1 → atomistics-0.2.2}/pyproject.toml +5 -2
  9. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_eos.py +71 -0
  10. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_sphinxdft.py +6 -6
  11. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_phonons_ase_emt.py +20 -0
  12. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_phonons_lammpsfile.py +20 -0
  13. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_phonons_lammpslib.py +20 -0
  14. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_sphinx_parser.py +58 -20
  15. atomistics-0.2.2/tests/test_vasp_parser.py +68 -0
  16. {atomistics-0.2.1 → atomistics-0.2.2}/LICENSE +0 -0
  17. {atomistics-0.2.1 → atomistics-0.2.2}/MANIFEST.in +0 -0
  18. {atomistics-0.2.1 → atomistics-0.2.2}/README.md +0 -0
  19. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/__init__.py +0 -0
  20. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/__init__.py +0 -0
  21. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/ase.py +0 -0
  22. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/hessian.py +0 -0
  23. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/interface.py +0 -0
  24. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/__init__.py +0 -0
  25. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/commands.py +0 -0
  26. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/filecalculator.py +0 -0
  27. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/helpers.py +0 -0
  28. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/libcalculator.py +0 -0
  29. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/phonon.py +0 -0
  30. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/lammps/potential.py +0 -0
  31. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/qe.py +0 -0
  32. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/calculators/wrapper.py +0 -0
  33. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/referencedata/__init__.py +0 -0
  34. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/referencedata/mendeleev.py +0 -0
  35. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/referencedata/wikipedia.py +0 -0
  36. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/referencedata/wolframalpha.py +0 -0
  37. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/shared/__init__.py +0 -0
  38. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/shared/output.py +0 -0
  39. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/shared/parallel.py +0 -0
  40. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/shared/thermal_expansion.py +0 -0
  41. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/shared/tqdm_iterator.py +0 -0
  42. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/__init__.py +0 -0
  43. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/elastic/__init__.py +0 -0
  44. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/elastic/elastic_moduli.py +0 -0
  45. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/elastic/helper.py +0 -0
  46. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/elastic/symmetry.py +0 -0
  47. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/elastic/workflow.py +0 -0
  48. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/evcurve/__init__.py +0 -0
  49. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/evcurve/debye.py +0 -0
  50. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/evcurve/fit.py +0 -0
  51. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/evcurve/helper.py +0 -0
  52. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/evcurve/thermo.py +0 -0
  53. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/evcurve/workflow.py +0 -0
  54. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/interface.py +0 -0
  55. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/langevin.py +0 -0
  56. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/molecular_dynamics.py +0 -0
  57. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/phonons/__init__.py +0 -0
  58. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/phonons/helper.py +0 -0
  59. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/phonons/units.py +0 -0
  60. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/phonons/workflow.py +0 -0
  61. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/quasiharmonic.py +0 -0
  62. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics/workflows/structure_optimization.py +0 -0
  63. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics.egg-info/dependency_links.txt +0 -0
  64. {atomistics-0.2.1 → atomistics-0.2.2}/atomistics.egg-info/top_level.txt +0 -0
  65. {atomistics-0.2.1 → atomistics-0.2.2}/setup.cfg +0 -0
  66. {atomistics-0.2.1 → atomistics-0.2.2}/setup.py +0 -0
  67. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_abstract_workflow_class.py +0 -0
  68. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_ase_md_emt.py +0 -0
  69. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_calc_stress_ase_emt.py +0 -0
  70. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_calc_stress_lammpslib.py +0 -0
  71. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_elastic_ase_emt.py +0 -0
  72. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_elastic_helper.py +0 -0
  73. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_elastic_lammpslib.py +0 -0
  74. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_elastic_lammpslib_functional.py +0 -0
  75. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_ase_emt.py +0 -0
  76. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_ase_lammps.py +0 -0
  77. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_lammpsfile.py +0 -0
  78. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_lammpslib.py +0 -0
  79. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_lammpslib_function_parallel.py +0 -0
  80. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_lammpslib_functional.py +0 -0
  81. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_evcurve_qe.py +0 -0
  82. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_hessian_lammpslib.py +0 -0
  83. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_lammps_calculator.py +0 -0
  84. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_lammpslib_md.py +0 -0
  85. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_lammpslib_md_phonons.py +0 -0
  86. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_langevin_lammpslib.py +0 -0
  87. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_molecular_dynamics_thermal_expansion_lammpslib.py +0 -0
  88. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_optimize_positions_ase_emt.py +0 -0
  89. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_optimize_positions_lammpsfile.py +0 -0
  90. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_optimize_positions_lammpslib.py +0 -0
  91. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_phonons_lammpslib_functional.py +0 -0
  92. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_quasiharmonic_ase_emt.py +0 -0
  93. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_quasiharmonic_lammpslib.py +0 -0
  94. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_quasiharmonic_lammpslib_functional.py +0 -0
  95. {atomistics-0.2.1 → atomistics-0.2.2}/tests/test_referencedata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: atomistics
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Interfaces for atomistic simulation codes and workflows
5
5
  Author-email: Jan Janssen <janssen@mpie.de>
6
6
  License: BSD 3-Clause License
@@ -53,7 +53,7 @@ Requires-Dist: numpy==1.26.4
53
53
  Requires-Dist: scipy<=1.15.2,>=1.11.1
54
54
  Requires-Dist: spglib<=2.5.0,>=2.0.2
55
55
  Provides-Extra: phonopy
56
- Requires-Dist: phonopy<=2.37.0,>=2.20.0; extra == "phonopy"
56
+ Requires-Dist: phonopy<=2.38.0,>=2.20.0; extra == "phonopy"
57
57
  Requires-Dist: seekpath<=2.1.0,>=1.9.0; extra == "phonopy"
58
58
  Requires-Dist: structuretoolkit<=0.0.31,>=0.0.10; extra == "phonopy"
59
59
  Provides-Extra: gpaw
@@ -77,9 +77,11 @@ Requires-Dist: requests<=2.32.3,>=2.24.0; extra == "experimental"
77
77
  Provides-Extra: sphinxdft
78
78
  Requires-Dist: sphinx-parser==0.0.1; extra == "sphinxdft"
79
79
  Provides-Extra: qe
80
- Requires-Dist: pwtools==1.2.3; extra == "qe"
80
+ Requires-Dist: pwtools==1.3.0; extra == "qe"
81
81
  Provides-Extra: tqdm
82
82
  Requires-Dist: tqdm==4.67.1; extra == "tqdm"
83
+ Provides-Extra: vasp
84
+ Requires-Dist: pyiron_vasp==0.2.4; extra == "vasp"
83
85
 
84
86
  # atomistics
85
87
  [![Pipeline](https://github.com/pyiron/atomistics/actions/workflows/pipeline.yml/badge.svg)](https://github.com/pyiron/atomistics/actions/workflows/pipeline.yml)
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-02-26T23:14:56+0100",
11
+ "date": "2025-03-05T08:13:01+0100",
12
12
  "dirty": true,
13
13
  "error": null,
14
- "full-revisionid": "66b638e04263084c998d140d01f3e866e0b5a8cf",
15
- "version": "0.2.1"
14
+ "full-revisionid": "5b518f33f60f904bf2c88dff51b72a07fe99091c",
15
+ "version": "0.2.2"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -5,6 +5,7 @@ import scipy.constants
5
5
  from ase.atoms import Atoms
6
6
  from sphinx_parser.ase import get_structure_group, id_spx_to_ase
7
7
  from sphinx_parser.input import sphinx
8
+ from sphinx_parser.jobs import apply_minimization
8
9
  from sphinx_parser.output import collect_energy_dat, collect_eval_forces
9
10
  from sphinx_parser.potential import get_paw_from_structure
10
11
  from sphinx_parser.toolkit import to_sphinx
@@ -20,11 +21,10 @@ HARTREE_TO_EV = scipy.constants.physical_constants["Hartree energy in eV"][0]
20
21
  HARTREE_OVER_BOHR_TO_EV_OVER_ANGSTROM = HARTREE_TO_EV / BOHR_TO_ANGSTROM
21
22
 
22
23
 
23
- def _write_input(
24
+ def _generate_input(
24
25
  structure: Atoms,
25
- working_directory: str,
26
26
  maxSteps: int = 100,
27
- eCut: float = 25.0,
27
+ energy_cutoff_in_eV: float = 500.0,
28
28
  kpoint_coords: Optional[list[float, float, float]] = None,
29
29
  kpoint_folding: Optional[list[int, int, int]] = None,
30
30
  ):
@@ -39,7 +39,7 @@ def _write_input(
39
39
  )
40
40
  pawPot_group = get_paw_from_structure(structure)
41
41
  basis_group = sphinx.basis.create(
42
- eCut=eCut,
42
+ eCut=energy_cutoff_in_eV / HARTREE_TO_EV,
43
43
  kPoint=sphinx.basis.kPoint.create(coords=kpoint_coords),
44
44
  folding=kpoint_folding,
45
45
  )
@@ -50,7 +50,7 @@ def _write_input(
50
50
  ),
51
51
  rho=sphinx.initialGuess.rho.create(atomicOrbitals=True),
52
52
  )
53
- input_sx = sphinx.create(
53
+ return sphinx.create(
54
54
  pawPot=pawPot_group,
55
55
  structure=struct_group,
56
56
  main=main_group,
@@ -58,8 +58,6 @@ def _write_input(
58
58
  PAWHamiltonian=paw_group,
59
59
  initialGuess=initial_guess_group,
60
60
  )
61
- with open(os.path.join(working_directory, "input.sx"), "w") as f:
62
- f.write(to_sphinx(input_sx))
63
61
 
64
62
 
65
63
  class OutputParser:
@@ -90,12 +88,54 @@ class OutputParser:
90
88
  raise NotImplementedError()
91
89
 
92
90
 
91
+ def optimize_positions_with_sphinxdft(
92
+ structure: Atoms,
93
+ working_directory: str,
94
+ executable_function: callable,
95
+ max_electronic_steps: int = 100,
96
+ energy_cutoff_in_eV: float = 500.0,
97
+ mode: str = "linQN",
98
+ dEnergy: float = 1.0e-6,
99
+ max_ionic_steps: int = 50,
100
+ kpoint_coords: Optional[list[float, float, float]] = None,
101
+ kpoint_folding: Optional[list[int, int, int]] = None,
102
+ ) -> Atoms:
103
+ if kpoint_coords is None:
104
+ kpoint_coords = [0.5, 0.5, 0.5]
105
+ if kpoint_folding is None:
106
+ kpoint_folding = [3, 3, 3]
107
+ input_sx = _generate_input(
108
+ structure=structure,
109
+ maxSteps=max_electronic_steps,
110
+ energy_cutoff_in_eV=energy_cutoff_in_eV,
111
+ kpoint_coords=kpoint_coords,
112
+ kpoint_folding=kpoint_folding,
113
+ )
114
+ input_sx = apply_minimization(
115
+ sphinx_input=input_sx,
116
+ mode=mode,
117
+ dEnergy=dEnergy,
118
+ maxSteps=max_ionic_steps,
119
+ )
120
+ with open(os.path.join(working_directory, "input.sx"), "w") as f:
121
+ f.write(to_sphinx(input_sx))
122
+ executable_function(working_directory)
123
+ structure_copy = structure.copy()
124
+ structure_copy.positions = (
125
+ collect_eval_forces(os.path.join(working_directory, "relaxHist.sx"))[
126
+ "positions"
127
+ ][-1][id_spx_to_ase(structure)]
128
+ / BOHR_TO_ANGSTROM
129
+ )
130
+ return structure_copy
131
+
132
+
93
133
  def calc_static_with_sphinxdft(
94
134
  structure: Atoms,
95
135
  working_directory: str,
96
136
  executable_function: callable,
97
- maxSteps: int = 100,
98
- eCut: float = 25,
137
+ max_electronic_steps: int = 100,
138
+ energy_cutoff_in_eV: float = 500.0,
99
139
  kpoint_coords: Optional[list[float, float, float]] = None,
100
140
  kpoint_folding: Optional[list[int, int, int]] = None,
101
141
  output_keys=OutputStatic.keys(),
@@ -104,14 +144,15 @@ def calc_static_with_sphinxdft(
104
144
  kpoint_coords = [0.5, 0.5, 0.5]
105
145
  if kpoint_folding is None:
106
146
  kpoint_folding = [3, 3, 3]
107
- _write_input(
147
+ input_sx = _generate_input(
108
148
  structure=structure,
109
- working_directory=working_directory,
110
- maxSteps=maxSteps,
111
- eCut=eCut,
149
+ maxSteps=max_electronic_steps,
150
+ energy_cutoff_in_eV=energy_cutoff_in_eV,
112
151
  kpoint_coords=kpoint_coords,
113
152
  kpoint_folding=kpoint_folding,
114
153
  )
154
+ with open(os.path.join(working_directory, "input.sx"), "w") as f:
155
+ f.write(to_sphinx(input_sx))
115
156
  executable_function(working_directory)
116
157
  output_obj = OutputParser(working_directory=working_directory, structure=structure)
117
158
  result_dict = OutputStatic(
@@ -129,20 +170,37 @@ def evaluate_with_sphinx(
129
170
  tasks: list,
130
171
  working_directory: str,
131
172
  executable_function: callable,
132
- maxSteps: int = 100,
133
- eCut: float = 25,
173
+ max_electronic_steps: int = 100,
174
+ energy_cutoff_in_eV: float = 500,
134
175
  kpoint_coords: Optional[list[float, float, float]] = None,
135
176
  kpoint_folding: Optional[list[int, int, int]] = None,
177
+ sphinx_optimizer_kwargs: Optional[dict] = None,
136
178
  ) -> dict:
137
179
  if kpoint_coords is None:
138
180
  kpoint_coords = [0.5, 0.5, 0.5]
139
181
  if kpoint_folding is None:
140
182
  kpoint_folding = [3, 3, 3]
141
- if "calc_energy" in tasks or "calc_forces" in tasks or "calc_stress" in tasks:
183
+ if sphinx_optimizer_kwargs is None:
184
+ sphinx_optimizer_kwargs = {}
185
+ results = {}
186
+ if "optimize_positions" in tasks:
187
+ results["structure_with_optimized_positions"] = (
188
+ optimize_positions_with_sphinxdft(
189
+ structure=structure,
190
+ max_electronic_steps=max_electronic_steps,
191
+ energy_cutoff_in_eV=energy_cutoff_in_eV,
192
+ kpoint_coords=kpoint_coords,
193
+ kpoint_folding=kpoint_folding,
194
+ working_directory=working_directory,
195
+ executable_function=executable_function,
196
+ **sphinx_optimizer_kwargs,
197
+ )
198
+ )
199
+ elif "calc_energy" in tasks or "calc_forces" in tasks or "calc_stress" in tasks:
142
200
  return calc_static_with_sphinxdft(
143
201
  structure=structure,
144
- maxSteps=maxSteps,
145
- eCut=eCut,
202
+ max_electronic_steps=max_electronic_steps,
203
+ energy_cutoff_in_eV=energy_cutoff_in_eV,
146
204
  kpoint_coords=kpoint_coords,
147
205
  kpoint_folding=kpoint_folding,
148
206
  working_directory=working_directory,
@@ -151,3 +209,4 @@ def evaluate_with_sphinx(
151
209
  )
152
210
  else:
153
211
  raise ValueError("The SphinxDFT calculator does not implement:", tasks)
212
+ return results
@@ -0,0 +1,219 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ from ase.atoms import Atoms
5
+ from ase.calculators.vasp.create_input import GenerateVaspInput
6
+ from pyiron_vasp.vasp.output import parse_vasp_output
7
+
8
+ from atomistics.calculators.interface import get_quantities_from_tasks
9
+ from atomistics.calculators.wrapper import as_task_dict_evaluator
10
+ from atomistics.shared.output import OutputStatic
11
+
12
+
13
+ class OutputParser:
14
+ def __init__(self, working_directory, structure):
15
+ self._output_dict = parse_vasp_output(
16
+ working_directory=working_directory, structure=structure
17
+ )
18
+
19
+ def get_energy(self):
20
+ return self._output_dict["generic"]["energy_tot"][-1]
21
+
22
+ def get_forces(self):
23
+ return self._output_dict["generic"]["forces"][-1]
24
+
25
+ def get_volume(self):
26
+ return self._output_dict["generic"]["volume"][-1]
27
+
28
+ def get_stress(self):
29
+ return self._output_dict["generic"]["stresses"][-1]
30
+
31
+
32
+ def write_input(working_directory, atoms, **kwargs):
33
+ vip = GenerateVaspInput()
34
+ vip.set(**kwargs)
35
+ vip.initialize(atoms=atoms)
36
+ os.makedirs(working_directory, exist_ok=True)
37
+ vip.write_input(atoms=atoms, directory=working_directory)
38
+
39
+
40
+ def calc_static_with_vasp(
41
+ structure: Atoms,
42
+ working_directory: str,
43
+ executable_function: callable,
44
+ prec: str = "Accurate",
45
+ algo: str = "Fast",
46
+ lreal: bool = False,
47
+ lwave: bool = False,
48
+ lorbit: int = 0,
49
+ kpts: Optional[list[int, int, int]] = None,
50
+ output_keys: dict = OutputStatic.keys(),
51
+ **kwargs,
52
+ ) -> dict:
53
+ if kpts is None:
54
+ kpts = [4, 4, 4]
55
+ write_input(
56
+ working_directory=working_directory,
57
+ atoms=structure,
58
+ prec=prec,
59
+ algo=algo,
60
+ lreal=lreal,
61
+ lwave=lwave,
62
+ lorbit=lorbit,
63
+ kpts=kpts,
64
+ **kwargs,
65
+ )
66
+ executable_function(working_directory)
67
+ output_obj = OutputParser(working_directory=working_directory, structure=structure)
68
+ result_dict = OutputStatic(
69
+ forces=output_obj.get_forces,
70
+ energy=output_obj.get_energy,
71
+ stress=output_obj.get_stress,
72
+ volume=output_obj.get_volume,
73
+ ).get(output_keys=output_keys)
74
+ return result_dict
75
+
76
+
77
+ def optimize_positions_with_vasp(
78
+ structure: Atoms,
79
+ working_directory: str,
80
+ executable_function: callable,
81
+ prec: str = "Accurate",
82
+ algo: str = "Fast",
83
+ lreal: bool = False,
84
+ lwave: bool = False,
85
+ lorbit: int = 0,
86
+ isif: int = 2,
87
+ ibrion: int = 2,
88
+ nsw: int = 100,
89
+ kpts: Optional[list[int, int, int]] = None,
90
+ **kwargs,
91
+ ) -> Atoms:
92
+ if kpts is None:
93
+ kpts = [4, 4, 4]
94
+ write_input(
95
+ working_directory=working_directory,
96
+ atoms=structure,
97
+ prec=prec,
98
+ algo=algo,
99
+ lreal=lreal,
100
+ lwave=lwave,
101
+ lorbit=lorbit,
102
+ kpts=kpts,
103
+ isif=isif,
104
+ ibrion=ibrion,
105
+ nsw=nsw,
106
+ **kwargs,
107
+ )
108
+ executable_function(working_directory)
109
+ output_dict = parse_vasp_output(
110
+ working_directory=working_directory, structure=structure
111
+ )
112
+ structure_copy = structure.copy()
113
+ structure_copy.positions = output_dict["generic"]["positions"][-1]
114
+ return structure_copy
115
+
116
+
117
+ def optimize_positions_and_volume_with_vasp(
118
+ structure: Atoms,
119
+ working_directory: str,
120
+ executable_function: callable,
121
+ prec: str = "Accurate",
122
+ algo: str = "Fast",
123
+ lreal: bool = False,
124
+ lwave: bool = False,
125
+ lorbit: int = 0,
126
+ isif: int = 3,
127
+ ibrion: int = 2,
128
+ nsw: int = 100,
129
+ kpts: Optional[list[int, int, int]] = None,
130
+ **kwargs,
131
+ ) -> Atoms:
132
+ if kpts is None:
133
+ kpts = [4, 4, 4]
134
+ write_input(
135
+ working_directory=working_directory,
136
+ atoms=structure,
137
+ prec=prec,
138
+ algo=algo,
139
+ lreal=lreal,
140
+ lwave=lwave,
141
+ lorbit=lorbit,
142
+ kpts=kpts,
143
+ isif=isif,
144
+ ibrion=ibrion,
145
+ nsw=nsw,
146
+ **kwargs,
147
+ )
148
+ executable_function(working_directory)
149
+ output_dict = parse_vasp_output(
150
+ working_directory=working_directory, structure=structure
151
+ )
152
+ structure_copy = structure.copy()
153
+ structure_copy.set_cell(output_dict["generic"]["cells"][-1], scale_atoms=True)
154
+ structure_copy.positions = output_dict["generic"]["positions"][-1]
155
+ return structure_copy
156
+
157
+
158
+ @as_task_dict_evaluator
159
+ def evaluate_with_vasp(
160
+ structure: Atoms,
161
+ tasks: list,
162
+ working_directory: str,
163
+ executable_function: callable,
164
+ prec: str = "Accurate",
165
+ algo: str = "Fast",
166
+ lreal: bool = False,
167
+ lwave: bool = False,
168
+ lorbit: int = 0,
169
+ kpts: Optional[list[int, int, int]] = None,
170
+ **kwargs,
171
+ ) -> dict:
172
+ if kpts is None:
173
+ kpts = [4, 4, 4]
174
+ results = {}
175
+ if "optimize_positions_and_volume" in tasks:
176
+ results["structure_with_optimized_positions_and_volume"] = (
177
+ optimize_positions_and_volume_with_vasp(
178
+ structure=structure,
179
+ working_directory=working_directory,
180
+ executable_function=executable_function,
181
+ prec=prec,
182
+ algo=algo,
183
+ lreal=lreal,
184
+ lwave=lwave,
185
+ lorbit=lorbit,
186
+ kpts=kpts,
187
+ **kwargs,
188
+ )
189
+ )
190
+ elif "optimize_positions" in tasks:
191
+ results["structure_with_optimized_positions"] = optimize_positions_with_vasp(
192
+ structure=structure,
193
+ working_directory=working_directory,
194
+ executable_function=executable_function,
195
+ prec=prec,
196
+ algo=algo,
197
+ lreal=lreal,
198
+ lwave=lwave,
199
+ lorbit=lorbit,
200
+ kpts=kpts,
201
+ **kwargs,
202
+ )
203
+ elif "calc_energy" in tasks or "calc_forces" in tasks or "calc_stress" in tasks:
204
+ return calc_static_with_vasp(
205
+ structure=structure,
206
+ working_directory=working_directory,
207
+ executable_function=executable_function,
208
+ prec=prec,
209
+ algo=algo,
210
+ lreal=lreal,
211
+ lwave=lwave,
212
+ lorbit=lorbit,
213
+ kpts=kpts,
214
+ output_keys=get_quantities_from_tasks(tasks=tasks),
215
+ **kwargs,
216
+ )
217
+ else:
218
+ raise ValueError("The VASP calculator does not implement:", tasks)
219
+ return results
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: atomistics
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Interfaces for atomistic simulation codes and workflows
5
5
  Author-email: Jan Janssen <janssen@mpie.de>
6
6
  License: BSD 3-Clause License
@@ -53,7 +53,7 @@ Requires-Dist: numpy==1.26.4
53
53
  Requires-Dist: scipy<=1.15.2,>=1.11.1
54
54
  Requires-Dist: spglib<=2.5.0,>=2.0.2
55
55
  Provides-Extra: phonopy
56
- Requires-Dist: phonopy<=2.37.0,>=2.20.0; extra == "phonopy"
56
+ Requires-Dist: phonopy<=2.38.0,>=2.20.0; extra == "phonopy"
57
57
  Requires-Dist: seekpath<=2.1.0,>=1.9.0; extra == "phonopy"
58
58
  Requires-Dist: structuretoolkit<=0.0.31,>=0.0.10; extra == "phonopy"
59
59
  Provides-Extra: gpaw
@@ -77,9 +77,11 @@ Requires-Dist: requests<=2.32.3,>=2.24.0; extra == "experimental"
77
77
  Provides-Extra: sphinxdft
78
78
  Requires-Dist: sphinx-parser==0.0.1; extra == "sphinxdft"
79
79
  Provides-Extra: qe
80
- Requires-Dist: pwtools==1.2.3; extra == "qe"
80
+ Requires-Dist: pwtools==1.3.0; extra == "qe"
81
81
  Provides-Extra: tqdm
82
82
  Requires-Dist: tqdm==4.67.1; extra == "tqdm"
83
+ Provides-Extra: vasp
84
+ Requires-Dist: pyiron_vasp==0.2.4; extra == "vasp"
83
85
 
84
86
  # atomistics
85
87
  [![Pipeline](https://github.com/pyiron/atomistics/actions/workflows/pipeline.yml/badge.svg)](https://github.com/pyiron/atomistics/actions/workflows/pipeline.yml)
@@ -16,6 +16,7 @@ atomistics/calculators/hessian.py
16
16
  atomistics/calculators/interface.py
17
17
  atomistics/calculators/qe.py
18
18
  atomistics/calculators/sphinxdft.py
19
+ atomistics/calculators/vasp.py
19
20
  atomistics/calculators/wrapper.py
20
21
  atomistics/calculators/lammps/__init__.py
21
22
  atomistics/calculators/lammps/commands.py
@@ -88,4 +89,5 @@ tests/test_quasiharmonic_ase_emt.py
88
89
  tests/test_quasiharmonic_lammpslib.py
89
90
  tests/test_quasiharmonic_lammpslib_functional.py
90
91
  tests/test_referencedata.py
91
- tests/test_sphinx_parser.py
92
+ tests/test_sphinx_parser.py
93
+ tests/test_vasp_parser.py
@@ -26,15 +26,18 @@ dynaphopy<=1.17.16,>=1.17.5
26
26
  pyiron_lammps<=0.4.0,>=0.3.2
27
27
 
28
28
  [phonopy]
29
- phonopy<=2.37.0,>=2.20.0
29
+ phonopy<=2.38.0,>=2.20.0
30
30
  seekpath<=2.1.0,>=1.9.0
31
31
  structuretoolkit<=0.0.31,>=0.0.10
32
32
 
33
33
  [qe]
34
- pwtools==1.2.3
34
+ pwtools==1.3.0
35
35
 
36
36
  [sphinxdft]
37
37
  sphinx-parser==0.0.1
38
38
 
39
39
  [tqdm]
40
40
  tqdm==4.67.1
41
+
42
+ [vasp]
43
+ pyiron_vasp==0.2.4
@@ -38,7 +38,7 @@ Repository = "https://github.com/pyiron/atomistics"
38
38
 
39
39
  [project.optional-dependencies]
40
40
  phonopy = [
41
- "phonopy>=2.20.0,<=2.37.0",
41
+ "phonopy>=2.20.0,<=2.38.0",
42
42
  "seekpath>=1.9.0,<=2.1.0",
43
43
  "structuretoolkit>=0.0.10,<=0.0.31",
44
44
  ]
@@ -68,11 +68,14 @@ sphinxdft = [
68
68
  "sphinx-parser==0.0.1"
69
69
  ]
70
70
  qe = [
71
- "pwtools==1.2.3"
71
+ "pwtools==1.3.0"
72
72
  ]
73
73
  tqdm = [
74
74
  "tqdm==4.67.1"
75
75
  ]
76
+ vasp = [
77
+ "pyiron_vasp==0.2.4"
78
+ ]
76
79
 
77
80
  [tool.setuptools.packages.find]
78
81
  include = ["atomistics*"]
@@ -1,8 +1,11 @@
1
+ from ase.build import bulk
1
2
  import numpy as np
2
3
 
3
4
  import unittest
4
5
 
5
6
  from atomistics.workflows.evcurve.fit import fit_equation_of_state
7
+ from atomistics.workflows.evcurve.debye import DebyeModel
8
+ from atomistics.workflows.evcurve.thermo import get_thermo_bulk_model
6
9
 
7
10
 
8
11
  class TestEvCurve(unittest.TestCase):
@@ -72,6 +75,74 @@ class TestEvCurve(unittest.TestCase):
72
75
  fit_dict = fit_equation_of_state(
73
76
  volume_lst=self.volumes, energy_lst=self.energies, fittype="vinet"
74
77
  )
78
+ print(fit_dict)
75
79
  self.assertAlmostEqual(fit_dict["volume_eq"], 66.43032532814925)
76
80
  self.assertAlmostEqual(fit_dict["bulkmodul_eq"], 77.61265363975706)
77
81
  self.assertAlmostEqual(fit_dict["b_prime_eq"], 1.2734991618131122)
82
+
83
+
84
+ class TestThermo(unittest.TestCase):
85
+ def setUp(self):
86
+ self._volumes = [
87
+ 63.10883669478296,
88
+ 63.77314023893856,
89
+ 64.43744378309412,
90
+ 65.10174732724975,
91
+ 65.7660508714054,
92
+ 66.43035441556098,
93
+ 67.09465795971657,
94
+ 67.7589615038722,
95
+ 68.42326504802779,
96
+ 69.08756859218344,
97
+ 69.75187213633905,
98
+ ]
99
+ self._thermo = get_thermo_bulk_model(
100
+ temperatures=np.arange(1.0, 1550.0, 50.0),
101
+ debye_model=DebyeModel(
102
+ fit_dict={
103
+ "energy_eq": -13.439961346483864,
104
+ "volume_eq": 66.43032532814925,
105
+ "bulkmodul_eq": 77.61265363975706,
106
+ "b_prime_eq": 1.2734991618131122,
107
+ "volume": self._volumes,
108
+ "fit_dict": {
109
+ "fit_type": "vinet",
110
+ }
111
+ },
112
+ masses=bulk("Al", cubic=True).get_masses(),
113
+ num_steps=50,
114
+ ),
115
+ )
116
+
117
+ def test_copy(self):
118
+ thermo_copy = self._thermo.copy()
119
+ self.assertEqual(len(thermo_copy.__dict__), len(self._thermo.__dict__))
120
+
121
+ def test_num_atoms(self):
122
+ self.assertEqual(self._thermo.num_atoms, 1)
123
+ self._thermo.num_atoms = 2
124
+ self.assertEqual(self._thermo.num_atoms, 2)
125
+ self._thermo.num_atoms = 1
126
+
127
+ def test_d_temp(self):
128
+ self.assertEqual(self._thermo._d_temp, 50.0)
129
+
130
+ def test_d_vol(self):
131
+ self.assertTrue(np.isclose(self._thermo._d_vol, 0.13557215186849447))
132
+
133
+ def test_volumes(self):
134
+ self.assertTrue(np.isclose(self._thermo.volumes[0], self._volumes[0]))
135
+ self.assertTrue(np.isclose(self._thermo.volumes[-1], self._volumes[-1]))
136
+ self.assertEqual(len(self._thermo.volumes), 50)
137
+
138
+ def test_entropy(self):
139
+ self._thermo._entropy = None
140
+ self.assertEqual(self._thermo.entropy.shape[0], 31)
141
+ self.assertEqual(self._thermo.entropy.shape[1], 50)
142
+ self._thermo._entropy = None
143
+
144
+ def test_pressure(self):
145
+ self._thermo._pressure = None
146
+ self.assertEqual(self._thermo.pressure.shape[0], 31)
147
+ self.assertEqual(self._thermo.pressure.shape[1], 50)
148
+ self._thermo._pressure = None
@@ -19,10 +19,10 @@ def validate_fitdict(fit_dict):
19
19
  lst = [
20
20
  fit_dict["bulkmodul_eq"] > 55,
21
21
  fit_dict["bulkmodul_eq"] < 65,
22
- fit_dict["energy_eq"] > -227.76,
23
- fit_dict["energy_eq"] < -227.75,
24
- fit_dict["volume_eq"] > 70.37,
25
- fit_dict["volume_eq"] < 70.38,
22
+ fit_dict["energy_eq"] > -227.72,
23
+ fit_dict["energy_eq"] < -227.71,
24
+ fit_dict["volume_eq"] > 70.92,
25
+ fit_dict["volume_eq"] < 70.93,
26
26
  ]
27
27
  if not all(lst):
28
28
  print(fit_dict)
@@ -57,8 +57,8 @@ class TestEvCurve(unittest.TestCase):
57
57
  task_dict=task_dict,
58
58
  working_directory=os.path.abspath("."),
59
59
  executable_function=evaluate_sphinx,
60
- maxSteps=100,
61
- eCut=25,
60
+ max_electronic_steps=100,
61
+ energy_cutoff_in_eV=500.0,
62
62
  kpoint_coords=[0.5, 0.5, 0.5],
63
63
  kpoint_folding=[3, 3, 3],
64
64
  )
@@ -39,3 +39,23 @@ class TestPhonons(unittest.TestCase):
39
39
  self.assertTrue("group_velocities" in mesh_dict.keys())
40
40
  self.assertTrue("frequency_points" in dos_dict.keys())
41
41
  self.assertTrue("total_dos" in dos_dict.keys())
42
+ dynmat_shape = workflow.get_dynamical_matrix().shape
43
+ self.assertEqual(dynmat_shape[0], 12)
44
+ self.assertEqual(dynmat_shape[1], 12)
45
+ hessmat_shape = workflow.get_hesse_matrix().shape
46
+ self.assertEqual(hessmat_shape[0], 324)
47
+ self.assertEqual(hessmat_shape[1], 324)
48
+ band_dict = workflow.get_band_structure()
49
+ self.assertEqual(len(band_dict['qpoints']), 6)
50
+ for vec in band_dict['qpoints']:
51
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
52
+ self.assertEqual(vec.shape[1], 3)
53
+ self.assertEqual(len(band_dict['distances']), 6)
54
+ for vec in band_dict['distances']:
55
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
56
+ self.assertEqual(len(band_dict['frequencies']), 6)
57
+ for vec in band_dict['frequencies']:
58
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
59
+ self.assertEqual(vec.shape[1], 12)
60
+ self.assertIsNone(band_dict['eigenvectors'])
61
+ self.assertIsNone(band_dict['group_velocities'])
@@ -131,3 +131,23 @@ class TestPhonons(unittest.TestCase):
131
131
  self.assertTrue(thermal_dict["free_energy"][0] > 0.1)
132
132
  self.assertTrue(thermal_dict["free_energy"][-1] < -2.6)
133
133
  self.assertTrue(thermal_dict["free_energy"][-1] > -2.7)
134
+ dynmat_shape = workflow.get_dynamical_matrix().shape
135
+ self.assertEqual(dynmat_shape[0], 12)
136
+ self.assertEqual(dynmat_shape[1], 12)
137
+ hessmat_shape = workflow.get_hesse_matrix().shape
138
+ self.assertEqual(hessmat_shape[0], 324)
139
+ self.assertEqual(hessmat_shape[1], 324)
140
+ band_dict = workflow.get_band_structure()
141
+ self.assertEqual(len(band_dict['qpoints']), 6)
142
+ for vec in band_dict['qpoints']:
143
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
144
+ self.assertEqual(vec.shape[1], 3)
145
+ self.assertEqual(len(band_dict['distances']), 6)
146
+ for vec in band_dict['distances']:
147
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
148
+ self.assertEqual(len(band_dict['frequencies']), 6)
149
+ for vec in band_dict['frequencies']:
150
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
151
+ self.assertEqual(vec.shape[1], 12)
152
+ self.assertIsNone(band_dict['eigenvectors'])
153
+ self.assertIsNone(band_dict['group_velocities'])
@@ -107,3 +107,23 @@ class TestPhonons(unittest.TestCase):
107
107
  self.assertTrue(thermal_dict["free_energy"][0] > 0.1)
108
108
  self.assertTrue(thermal_dict["free_energy"][-1] < -2.6)
109
109
  self.assertTrue(thermal_dict["free_energy"][-1] > -2.7)
110
+ dynmat_shape = workflow.get_dynamical_matrix().shape
111
+ self.assertEqual(dynmat_shape[0],12)
112
+ self.assertEqual(dynmat_shape[1], 12)
113
+ hessmat_shape = workflow.get_hesse_matrix().shape
114
+ self.assertEqual(hessmat_shape[0], 324)
115
+ self.assertEqual(hessmat_shape[1], 324)
116
+ band_dict = workflow.get_band_structure()
117
+ self.assertEqual(len(band_dict['qpoints']), 6)
118
+ for vec in band_dict['qpoints']:
119
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
120
+ self.assertEqual(vec.shape[1], 3)
121
+ self.assertEqual(len(band_dict['distances']), 6)
122
+ for vec in band_dict['distances']:
123
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
124
+ self.assertEqual(len(band_dict['frequencies']), 6)
125
+ for vec in band_dict['frequencies']:
126
+ self.assertTrue(vec.shape[0] in [34, 39, 78, 101, 95])
127
+ self.assertEqual(vec.shape[1], 12)
128
+ self.assertIsNone(band_dict['eigenvectors'])
129
+ self.assertIsNone(band_dict['group_velocities'])
@@ -6,7 +6,17 @@ import numpy as np
6
6
  from ase.build import bulk
7
7
 
8
8
  try:
9
- from atomistics.calculators.sphinxdft import OutputParser, _write_input, calc_static_with_sphinxdft, evaluate_with_sphinx, HARTREE_TO_EV, HARTREE_OVER_BOHR_TO_EV_OVER_ANGSTROM, BOHR_TO_ANGSTROM
9
+ from sphinx_parser.toolkit import to_sphinx
10
+ from atomistics.calculators.sphinxdft import (
11
+ OutputParser,
12
+ _generate_input,
13
+ calc_static_with_sphinxdft,
14
+ optimize_positions_with_sphinxdft,
15
+ evaluate_with_sphinx,
16
+ HARTREE_TO_EV,
17
+ HARTREE_OVER_BOHR_TO_EV_OVER_ANGSTROM,
18
+ BOHR_TO_ANGSTROM,
19
+ )
10
20
 
11
21
  skip_sphinx_test = False
12
22
  except ImportError:
@@ -37,30 +47,25 @@ class TestSphinxParser(unittest.TestCase):
37
47
  def test_write_input(self):
38
48
  folder = "test"
39
49
  os.makedirs(folder, exist_ok=True)
40
- _write_input(
50
+ input_sx = to_sphinx(_generate_input(
41
51
  structure=self._structure,
42
- working_directory=folder,
43
52
  maxSteps=100,
44
- eCut=25.0,
53
+ energy_cutoff_in_eV=25.0 * HARTREE_TO_EV,
45
54
  kpoint_coords=None,
46
55
  kpoint_folding=None,
47
- )
48
- file = os.path.join(folder, "input.sx")
49
- self.assertTrue(os.path.exists(file))
50
- with open(file, "r") as f:
51
- lines = f.readlines()
56
+ ))
52
57
 
53
58
  self.assertEqual(
54
- " eCut = 25.0;\n",
55
- "".join([l for l in lines if "eCut" in l])
59
+ " eCut = " + str(25.0 * HARTREE_TO_EV / HARTREE_TO_EV) + ";",
60
+ "".join([l for l in input_sx.split("\n") if "eCut" in l])
56
61
  )
57
62
  self.assertEqual(
58
- " folding = [3, 3, 3];\n",
59
- "".join([l for l in lines if "folding" in l])
63
+ " folding = [3, 3, 3];",
64
+ "".join([l for l in input_sx.split("\n") if "folding" in l])
60
65
  )
61
66
  self.assertEqual(
62
- " potType = \"AtomPAW\";\n",
63
- "".join([l for l in lines if "potType" in l])
67
+ " potType = \"AtomPAW\";",
68
+ "".join([l for l in input_sx.split("\n") if "potType" in l])
64
69
  )
65
70
  shutil.rmtree(folder)
66
71
 
@@ -69,8 +74,8 @@ class TestSphinxParser(unittest.TestCase):
69
74
  structure=self._structure,
70
75
  working_directory=self._output_directory,
71
76
  executable_function=executable_function,
72
- maxSteps=100,
73
- eCut=25,
77
+ max_electronic_steps=100,
78
+ energy_cutoff_in_eV=25.0 * HARTREE_TO_EV,
74
79
  kpoint_coords=None,
75
80
  kpoint_folding=None,
76
81
  output_keys=["volume", "forces", "energy"],
@@ -82,13 +87,28 @@ class TestSphinxParser(unittest.TestCase):
82
87
  np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]),
83
88
  )))
84
89
 
85
- def test_evaluate_with_sphinx(self):
90
+ def test_optimize_positions_with_sphinxdft(self):
91
+ structure_result = optimize_positions_with_sphinxdft(
92
+ structure=self._structure,
93
+ working_directory=self._output_directory,
94
+ executable_function=executable_function,
95
+ max_electronic_steps=100,
96
+ energy_cutoff_in_eV=25.0 * HARTREE_TO_EV,
97
+ kpoint_coords=None,
98
+ kpoint_folding=None,
99
+ mode="linQN",
100
+ dEnergy=1.0e-6,
101
+ max_ionic_steps=50,
102
+ )
103
+ self.assertEqual(len(structure_result), len(self._structure))
104
+
105
+ def test_evaluate_with_sphinx_energy_and_forces(self):
86
106
  results = evaluate_with_sphinx(
87
107
  task_dict={"calc_energy": self._structure, "calc_forces": self._structure},
88
108
  working_directory=self._output_directory,
89
109
  executable_function=executable_function,
90
- maxSteps=100,
91
- eCut=25,
110
+ max_electronic_steps=100,
111
+ energy_cutoff_in_eV=25.0 * HARTREE_TO_EV,
92
112
  kpoint_coords=None,
93
113
  kpoint_folding=None,
94
114
  )
@@ -97,3 +117,21 @@ class TestSphinxParser(unittest.TestCase):
97
117
  results["forces"],
98
118
  np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]),
99
119
  )))
120
+
121
+ def test_evaluate_with_sphinx_structure_optimization(self):
122
+ results = evaluate_with_sphinx(
123
+ task_dict={"optimize_positions": self._structure},
124
+ working_directory=self._output_directory,
125
+ executable_function=executable_function,
126
+ max_electronic_steps=100,
127
+ energy_cutoff_in_eV=25.0 * HARTREE_TO_EV,
128
+ kpoint_coords=None,
129
+ kpoint_folding=None,
130
+ sphinx_optimizer_kwargs={
131
+ "mode": "linQN",
132
+ "dEnergy": 1.0e-6,
133
+ "max_ionic_steps": 50,
134
+ },
135
+ )
136
+ self.assertTrue("structure_with_optimized_positions" in results.keys())
137
+ self.assertEqual(len(results), 1)
@@ -0,0 +1,68 @@
1
+ import os
2
+ import shutil
3
+ from unittest import TestCase
4
+
5
+ from ase.build import bulk
6
+
7
+ from atomistics.calculators.vasp import (
8
+ evaluate_with_vasp,
9
+ )
10
+ from atomistics.workflows import optimize_positions, optimize_positions_and_volume
11
+
12
+
13
+ os.environ["VASP_PP_PATH"] = os.path.abspath(os.path.join(__file__, "..", "static", "vasp"))
14
+
15
+ def copy_file_funct(working_directory):
16
+ outcar_file = os.path.abspath(os.path.join(__file__, "..", "static", "vasp", "OUTCAR"))
17
+ shutil.copy(outcar_file, working_directory)
18
+
19
+
20
+ class TestVaspParser(TestCase):
21
+ def setUp(self):
22
+ self._structure = bulk("Al", a=4.05, cubic=True)
23
+
24
+ def test_optimize_positions_with_vasp_task(self):
25
+ result = evaluate_with_vasp(
26
+ task_dict=optimize_positions(self._structure),
27
+ working_directory="opt_pos_task",
28
+ executable_function=copy_file_funct,
29
+ prec="Accurate",
30
+ algo="Fast",
31
+ lreal=False,
32
+ lwave=False,
33
+ lorbit=0,
34
+ xc="pbe"
35
+ )
36
+ self.assertEqual(len(result), 1)
37
+
38
+ def test_optimize_positions_and_volume_with_vasp_task(self):
39
+ result = evaluate_with_vasp(
40
+ task_dict=optimize_positions_and_volume(self._structure),
41
+ working_directory="opt_vol_task",
42
+ executable_function=copy_file_funct,
43
+ prec="Accurate",
44
+ algo="Fast",
45
+ lreal=False,
46
+ lwave=False,
47
+ lorbit=0,
48
+ xc="pbe"
49
+ )
50
+ self.assertEqual(len(result), 1)
51
+
52
+ def test_calc_static_task(self):
53
+ result = evaluate_with_vasp(
54
+ task_dict={
55
+ "calc_energy": self._structure,
56
+ "calc_forces": self._structure,
57
+ "calc_stress": self._structure,
58
+ },
59
+ working_directory="opt_vol_task",
60
+ executable_function=copy_file_funct,
61
+ prec="Accurate",
62
+ algo="Fast",
63
+ lreal=False,
64
+ lwave=False,
65
+ lorbit=0,
66
+ xc="pbe"
67
+ )
68
+ self.assertEqual(len(result), 3)
File without changes
File without changes
File without changes
File without changes
File without changes