pyfhiaims 0.0.3__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 (56) hide show
  1. pyfhiaims-0.0.3/PKG-INFO +82 -0
  2. pyfhiaims-0.0.3/README.md +56 -0
  3. pyfhiaims-0.0.3/pyfhiaims/__init__.py +5 -0
  4. pyfhiaims-0.0.3/pyfhiaims/aims_input.py +49 -0
  5. pyfhiaims-0.0.3/pyfhiaims/calculation.py +15 -0
  6. pyfhiaims-0.0.3/pyfhiaims/cli/__init__.py +0 -0
  7. pyfhiaims-0.0.3/pyfhiaims/control/__init__.py +1 -0
  8. pyfhiaims-0.0.3/pyfhiaims/control/chunk.py +31 -0
  9. pyfhiaims-0.0.3/pyfhiaims/control/control.py +291 -0
  10. pyfhiaims-0.0.3/pyfhiaims/control/cube.py +210 -0
  11. pyfhiaims-0.0.3/pyfhiaims/control/kpoints.py +16 -0
  12. pyfhiaims-0.0.3/pyfhiaims/errors.py +9 -0
  13. pyfhiaims-0.0.3/pyfhiaims/geometry/__init__.py +1 -0
  14. pyfhiaims-0.0.3/pyfhiaims/geometry/atom.py +264 -0
  15. pyfhiaims-0.0.3/pyfhiaims/geometry/geometry.py +602 -0
  16. pyfhiaims-0.0.3/pyfhiaims/output_parser/__init__.py +0 -0
  17. pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_out_calculation_section.py +630 -0
  18. pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_out_header_section.py +335 -0
  19. pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_out_section.py +108 -0
  20. pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_outputs.py +165 -0
  21. pyfhiaims-0.0.3/pyfhiaims/outputs/__init__.py +0 -0
  22. pyfhiaims-0.0.3/pyfhiaims/outputs/band_structure.py +13 -0
  23. pyfhiaims-0.0.3/pyfhiaims/outputs/parser/__init__.py +1 -0
  24. pyfhiaims-0.0.3/pyfhiaims/outputs/parser/abc.py +370 -0
  25. pyfhiaims-0.0.3/pyfhiaims/outputs/parser/converters.py +104 -0
  26. pyfhiaims-0.0.3/pyfhiaims/outputs/parser/parsers.py +435 -0
  27. pyfhiaims-0.0.3/pyfhiaims/outputs/parser/utils.py +36 -0
  28. pyfhiaims-0.0.3/pyfhiaims/outputs/stdout.py +75 -0
  29. pyfhiaims-0.0.3/pyfhiaims/species_defaults/__init__.py +0 -0
  30. pyfhiaims-0.0.3/pyfhiaims/species_defaults/basis_function.py +247 -0
  31. pyfhiaims-0.0.3/pyfhiaims/species_defaults/basis_set.py +275 -0
  32. pyfhiaims-0.0.3/pyfhiaims/species_defaults/electronic_configuration.py +69 -0
  33. pyfhiaims-0.0.3/pyfhiaims/species_defaults/integration_grid.py +154 -0
  34. pyfhiaims-0.0.3/pyfhiaims/species_defaults/species.py +360 -0
  35. pyfhiaims-0.0.3/pyfhiaims/utils/__init__.py +13 -0
  36. pyfhiaims-0.0.3/pyfhiaims/utils/typecast.py +21 -0
  37. pyfhiaims-0.0.3/pyfhiaims.egg-info/PKG-INFO +82 -0
  38. pyfhiaims-0.0.3/pyfhiaims.egg-info/SOURCES.txt +54 -0
  39. pyfhiaims-0.0.3/pyfhiaims.egg-info/dependency_links.txt +1 -0
  40. pyfhiaims-0.0.3/pyfhiaims.egg-info/requires.txt +13 -0
  41. pyfhiaims-0.0.3/pyfhiaims.egg-info/top_level.txt +3 -0
  42. pyfhiaims-0.0.3/pyproject.toml +168 -0
  43. pyfhiaims-0.0.3/setup.cfg +4 -0
  44. pyfhiaims-0.0.3/tests/__init__.py +5 -0
  45. pyfhiaims-0.0.3/tests/conftest.py +9 -0
  46. pyfhiaims-0.0.3/tests/test_basis_function.py +52 -0
  47. pyfhiaims-0.0.3/tests/test_basis_set.py +37 -0
  48. pyfhiaims-0.0.3/tests/test_electronic_configuration.py +31 -0
  49. pyfhiaims-0.0.3/tests/test_inputs/test_control.py +12 -0
  50. pyfhiaims-0.0.3/tests/test_inputs/test_geometry.py +291 -0
  51. pyfhiaims-0.0.3/tests/test_integration_grid.py +24 -0
  52. pyfhiaims-0.0.3/tests/test_parsers/test_parser.py +27 -0
  53. pyfhiaims-0.0.3/tests/test_parsers/test_sections.py +543 -0
  54. pyfhiaims-0.0.3/tests/test_stdout.py +22 -0
  55. pyfhiaims-0.0.3/tests/test_typecast.py +21 -0
  56. pyfhiaims-0.0.3/tests/utils.py +77 -0
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyfhiaims
3
+ Version: 0.0.3
4
+ Summary: An FHI-aims official Python package
5
+ Author-email: Thomas Purcell <purcell@fhi-berlin.mpg.de>, Andrei Sobolev <sobolev@ms1p.org>
6
+ Maintainer-email: Thomas Purcell <purcell@fhi-berlin.mpg.de>, Andrei Sobolev <sobolev@ms1p.org>
7
+ License: MIT
8
+ Project-URL: repository, https://gitlab.com/FHI-aims-club/pyfhiaims/
9
+ Keywords: electronic structure,FHI-aims
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: numpy<2.0
16
+ Requires-Dist: scipy>=1.1.1
17
+ Requires-Dist: monty~=2024.10.21
18
+ Requires-Dist: ase~=3.23
19
+ Provides-Extra: tests
20
+ Requires-Dist: pyyaml; extra == "tests"
21
+ Requires-Dist: pytest>=6.0; extra == "tests"
22
+ Requires-Dist: pytest-xdist>=1.31; extra == "tests"
23
+ Requires-Dist: pytest-cov>=2.8; extra == "tests"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pre-commit>=2.12.1; extra == "dev"
26
+
27
+ # pyfhiaims – an FHI-aims Python suite
28
+
29
+ ## Installation
30
+
31
+ You can install `pyfhiaims` from PyPI via `pip`:
32
+ ```bash
33
+ pip install pyfhiaims
34
+ ```
35
+
36
+ Or, alternatively, you can install it from the main branch of this Git repository:
37
+ ```bash
38
+ git clone https://gitlab.com/FHI-aims-club/pyfhiaims.git
39
+ cd pyfhiaims
40
+ pip install .
41
+ ```
42
+
43
+ ## Usage
44
+ The FHI-aims output file can be parsed in the following way:
45
+
46
+ ```python
47
+ from pyfhiaims import AimsStdout
48
+
49
+ stdout = AimsStdout("aims.out")
50
+ ```
51
+
52
+ Then you can access all the parsed results (`stdout.results`),
53
+ run metadata (runtime choices and some geometry statistics —
54
+ `stdout.metadata`), warnings (`stdout.warnings`), and errors.
55
+ Also, `stdout.is_finished_ok` tells if FHI-aims run has been finished
56
+ without any errors.
57
+
58
+ There are several properties defined that make access to different
59
+ widely used values easier, like `energy` and `forces`.
60
+ Also, the top level keys of `stdout.results` dictionary can be accessed
61
+ using dot notation (so run times can be accessed with
62
+ `stdout.final["time"]`).
63
+
64
+ There are many values that are parsed from the `aims.out` file; you are
65
+ welcome to explore `stdout.results` dictionary.
66
+
67
+ ## Support
68
+ Just write us an issue in the issue tracker!
69
+
70
+ ## Roadmap
71
+ To be written...
72
+
73
+ ## Contributing
74
+ Contributions are extremely welcome!
75
+
76
+ ## Authors and acknowledgment
77
+ The package was written by:
78
+ * Tom Purcell
79
+ * Andrei Sobolev
80
+
81
+ ## License
82
+ The project is licensed under MIT license.
@@ -0,0 +1,56 @@
1
+ # pyfhiaims – an FHI-aims Python suite
2
+
3
+ ## Installation
4
+
5
+ You can install `pyfhiaims` from PyPI via `pip`:
6
+ ```bash
7
+ pip install pyfhiaims
8
+ ```
9
+
10
+ Or, alternatively, you can install it from the main branch of this Git repository:
11
+ ```bash
12
+ git clone https://gitlab.com/FHI-aims-club/pyfhiaims.git
13
+ cd pyfhiaims
14
+ pip install .
15
+ ```
16
+
17
+ ## Usage
18
+ The FHI-aims output file can be parsed in the following way:
19
+
20
+ ```python
21
+ from pyfhiaims import AimsStdout
22
+
23
+ stdout = AimsStdout("aims.out")
24
+ ```
25
+
26
+ Then you can access all the parsed results (`stdout.results`),
27
+ run metadata (runtime choices and some geometry statistics —
28
+ `stdout.metadata`), warnings (`stdout.warnings`), and errors.
29
+ Also, `stdout.is_finished_ok` tells if FHI-aims run has been finished
30
+ without any errors.
31
+
32
+ There are several properties defined that make access to different
33
+ widely used values easier, like `energy` and `forces`.
34
+ Also, the top level keys of `stdout.results` dictionary can be accessed
35
+ using dot notation (so run times can be accessed with
36
+ `stdout.final["time"]`).
37
+
38
+ There are many values that are parsed from the `aims.out` file; you are
39
+ welcome to explore `stdout.results` dictionary.
40
+
41
+ ## Support
42
+ Just write us an issue in the issue tracker!
43
+
44
+ ## Roadmap
45
+ To be written...
46
+
47
+ ## Contributing
48
+ Contributions are extremely welcome!
49
+
50
+ ## Authors and acknowledgment
51
+ The package was written by:
52
+ * Tom Purcell
53
+ * Andrei Sobolev
54
+
55
+ ## License
56
+ The project is licensed under MIT license.
@@ -0,0 +1,5 @@
1
+ __version__ = "0.0.3"
2
+
3
+ from .calculation import AimsCalc
4
+ from .aims_input import AimsInput
5
+ from .outputs.stdout import AimsStdout
@@ -0,0 +1,49 @@
1
+ """An object representing FHI-aims input (control.in, geometry.in, k_list.in, T_bvk.in?)."""
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+ from pyfhiaims.errors import PyaimsError
7
+ from pyfhiaims.control import AimsControlIn
8
+ from pyfhiaims.control.kpoints import AimsKPoints
9
+ from pyfhiaims.geometry import AimsGeometry
10
+ from pyfhiaims.species_defaults.species import SpeciesDefaults
11
+
12
+
13
+ @dataclass
14
+ class AimsInput:
15
+ """
16
+ An object combining all input files for FHI-aims and providing methods
17
+ to deal with them consistently.
18
+ """
19
+ control: AimsControlIn
20
+ geometry: AimsGeometry = None
21
+ species_defaults: SpeciesDefaults | dict[str, SpeciesDefaults] = None
22
+
23
+ @classmethod
24
+ def from_folder(cls, folder: Path | str) -> "AimsInput":
25
+ """Get AimsInput object from a given folder."""
26
+ if isinstance(folder, str):
27
+ folder = Path(folder)
28
+ if not folder.is_dir():
29
+ raise PyaimsError("The given path is not a directory.")
30
+
31
+ control, geometry, k_points, species_defaults = [None, ] * 4
32
+ if (folder / "control.in").is_file():
33
+ control = AimsControlIn.from_file(folder / "control.in")
34
+ else:
35
+ raise PyaimsError(f"No control.in file found in {folder.as_posix()}.")
36
+
37
+ return cls(
38
+ control,
39
+ geometry,
40
+ )
41
+
42
+ @property
43
+ def k_points(self) -> AimsKPoints:
44
+ return self.control.k_points
45
+
46
+ @k_points.setter
47
+ def k_points(self, k_points: AimsKPoints) -> None:
48
+ self.control.k_points = k_points
49
+
@@ -0,0 +1,15 @@
1
+ """A main pyaims class representing Calculation"""
2
+ from pyfhiaims.aims_input import AimsInput
3
+ from pyfhiaims.control import AimsControlIn
4
+ from pyfhiaims.geometry import AimsGeometry
5
+ from pyfhiaims.outputs.parser import StdoutParser
6
+
7
+
8
+ class AimsCalc:
9
+ """An Aims calculation"""
10
+ aims_input: AimsInput
11
+
12
+ @classmethod
13
+ def from_folder(cls, folder, aims_stdout: str = "aims.out", aims_stderr: str = None):
14
+ """Get Aims Calculation from a folder with files."""
15
+
File without changes
@@ -0,0 +1 @@
1
+ from .control import AimsControlIn
@@ -0,0 +1,31 @@
1
+ """A class representing a chunk of control.in file"""
2
+
3
+ from dataclasses import dataclass
4
+ from inspect import isclass
5
+ from typing import Sequence
6
+
7
+ from monty.json import MSONable
8
+
9
+
10
+ @dataclass(kw_only=True)
11
+ class AimsControlChunk(MSONable):
12
+ keywords: tuple[str] = ()
13
+
14
+ def __init_subclass__(cls, **kwargs):
15
+ super().__init_subclass__(**kwargs)
16
+ keywords = []
17
+ if not cls.keywords:
18
+ for field in cls.__annotations__.values():
19
+ if isclass(field) and issubclass(field, AimsControlChunk):
20
+ keywords += list(field.keywords)
21
+ cls.keywords = tuple(keywords)
22
+
23
+ @classmethod
24
+ def from_strings(cls, config_str: Sequence[str]) -> "AimsControlChunk":
25
+ raise NotImplementedError
26
+
27
+ def to_string(self) -> str:
28
+ raise NotImplementedError
29
+
30
+ def __str__(self) -> str:
31
+ return self.to_string()
@@ -0,0 +1,291 @@
1
+ """Classes for reading/manipulating/writing FHI-aims control.in files."""
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ import time
6
+ from copy import deepcopy
7
+ from dataclasses import dataclass, field, fields
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING
10
+ from warnings import warn
11
+
12
+ from monty.json import MontyDecoder, MSONable
13
+
14
+ from pyfhiaims.control.kpoints import AimsKPoints
15
+ from pyfhiaims.errors import PyaimsError
16
+ from pyfhiaims.geometry.geometry import AimsGeometry
17
+ from pyfhiaims.species_defaults.species import SpeciesDefaults
18
+
19
+ if TYPE_CHECKING:
20
+ from typing import Any
21
+ from typing_extensions import Self
22
+
23
+ __author__ = "Thomas A. R. Purcell"
24
+ __version__ = "1.0"
25
+ __email__ = "purcellt@arizona.edu"
26
+ __date__ = "July 2024"
27
+
28
+
29
+ @dataclass
30
+ class AimsControlIn(MSONable):
31
+ """An FHI-aims control.in file.
32
+
33
+ Attributes:
34
+ parameters (dict[str, Any]): The parameters' dictionary containing all input
35
+ flags (key) and values for the control.in file
36
+ """
37
+
38
+ parameters: dict[str, Any] = field(default_factory=dict)
39
+ outputs: list[str] = field(default_factory=list)
40
+ k_points: AimsKPoints = None
41
+ species_defaults: dict[str, SpeciesDefaults] = field(default_factory=dict) # None?
42
+
43
+ def __getitem__(self, key: str) -> Any:
44
+ """Get an input parameter.
45
+
46
+ Args:
47
+ key (str): The parameter to get
48
+
49
+ Returns:
50
+ The setting for that parameter
51
+
52
+ Raises:
53
+ KeyError: If the key is not in self._parameters
54
+ """
55
+ if key == "output":
56
+ return self.outputs
57
+
58
+ if key not in self.parameters:
59
+ raise KeyError(f"{key} not set in AimsControlIn")
60
+ return self.parameters[key]
61
+
62
+ def __setitem__(self, key: str, value: Any) -> None:
63
+ """Set an attribute of the class.
64
+
65
+ Args:
66
+ key (str): The parameter to get
67
+ value (Any): The value for that parameter
68
+ """
69
+ if key == "output":
70
+ warn(
71
+ "Outputs are set seperately, use the outputs property",
72
+ RuntimeWarning,
73
+ stacklevel=1
74
+ )
75
+ else:
76
+ self.parameters[key] = value
77
+
78
+ def __delitem__(self, key: str) -> Any:
79
+ """Delete a parameter from the input object.
80
+
81
+ Args:
82
+ key (str): The key in the parameter to remove
83
+
84
+ Returns:
85
+ Either the value of the deleted parameter or None if key is
86
+ not in self._parameters
87
+ """
88
+ if key == "output":
89
+ self.outputs = []
90
+
91
+ return self.parameters.pop(key, None)
92
+
93
+ @staticmethod
94
+ def get_aims_control_parameter_str(key: str, value: Any, fmt: str) -> str:
95
+ """Get the string needed to add a parameter to the control.in file.
96
+
97
+ Args:
98
+ key (str): The name of the input flag
99
+ value (Any): The value to be set for the flag
100
+ fmt (str): The format string to apply to the value
101
+
102
+ Returns:
103
+ str: The line to add to the control.in file
104
+ """
105
+ if value is None:
106
+ return ""
107
+ return f"{key:50s}{fmt % value}\n"
108
+
109
+ def get_content(
110
+ self, geometry: AimsGeometry, verbose_header: bool = False, directory: str | Path | None = None
111
+ ) -> str:
112
+ """Get the content of the file.
113
+
114
+ Args:
115
+ geometry (AimsGeometry): The geometry to write the input file for
116
+ verbose_header (bool): If True print the input option dictionary
117
+ directory: str | Path | None = The directory for the calculation,
118
+
119
+ Returns:
120
+ str: The content of the file for a given geometry
121
+ """
122
+ parameters = deepcopy(self.parameters)
123
+
124
+ if directory is None:
125
+ directory = ""
126
+
127
+ lim = "#" + "=" * 79
128
+ content = ""
129
+
130
+ if parameters["xc"] == "LDA":
131
+ parameters["xc"] = "pw-lda"
132
+
133
+ cubes = parameters.pop("cubes", [])
134
+
135
+ if verbose_header:
136
+ content += "# \n# List of parameters used to initialize the calculator:"
137
+ for param, val in parameters.items():
138
+ content += f"# {param}:{val}\n"
139
+ content += f"{lim}\n"
140
+
141
+ if all([inp in parameters for inp in ["smearing", "occupation_type"]]):
142
+ raise ValueError("Both smearing and occupation_type can't be in the same parameters file.")
143
+
144
+ for key, value in parameters.items():
145
+ if key in ["species_dir", "plus_u"]:
146
+ continue
147
+ if key == "smearing":
148
+ name = parameters["smearing"][0].lower()
149
+ if name == "fermi-dirac":
150
+ name = "fermi"
151
+ width = parameters["smearing"][1]
152
+ if name == "methfessel-paxton":
153
+ order = parameters["smearing"][2]
154
+ order = " %d" % order
155
+ else:
156
+ order = ""
157
+
158
+ content += self.get_aims_control_parameter_str("occupation_type", (name, width, order), "%s %f%s")
159
+ elif key == "vdw_correction_hirshfeld" and value:
160
+ content += self.get_aims_control_parameter_str(key, "", "%s")
161
+ elif isinstance(value, bool):
162
+ content += self.get_aims_control_parameter_str(key, str(value).lower(), ".%s.")
163
+ elif isinstance(value, (tuple, list)):
164
+ content += self.get_aims_control_parameter_str(key, " ".join(map(str, value)), "%s")
165
+ elif isinstance(value, str):
166
+ content += self.get_aims_control_parameter_str(key, value, "%s")
167
+ else:
168
+ content += self.get_aims_control_parameter_str(key, value, "%r")
169
+
170
+ for output_type in self.outputs:
171
+ content += self.get_aims_control_parameter_str("output", output_type, "%s")
172
+
173
+ if cubes:
174
+ for cube in cubes:
175
+ content += cube.control_block
176
+
177
+ content += f"{lim}\n\n"
178
+
179
+ for sp in geometry.species_dict.values():
180
+ content += sp.content
181
+
182
+ return content
183
+
184
+ def write_file(
185
+ self,
186
+ geometry: AimsGeometry,
187
+ directory: str | Path | None = None,
188
+ verbose_header: bool = False,
189
+ overwrite: bool = False,
190
+ ) -> None:
191
+ """Write the control.in file.
192
+
193
+ Args:
194
+ geometry (AimsGeometry): The structure to write the input
195
+ file for
196
+ directory (str or Path): The directory to write the control.in file.
197
+ If None use cwd
198
+ verbose_header (bool): If True print the input option dictionary
199
+ overwrite (bool): If True allow to overwrite existing files
200
+
201
+ Raises:
202
+ ValueError: If a file must be overwritten and overwrite is False
203
+ ValueError: If k-grid is not provided for the periodic structures
204
+ """
205
+ directory = directory or Path.cwd()
206
+
207
+ if (Path(directory) / "control.in").exists() and not overwrite:
208
+ raise ValueError(f"control.in file already in {directory}")
209
+
210
+ if (geometry.lattice_vectors is not None) and (
211
+ "k_grid" not in self.parameters and "k_grid_density" not in self.parameters
212
+ ):
213
+ raise ValueError("k-grid must be defined for periodic systems")
214
+
215
+ content = self.get_content(geometry, verbose_header)
216
+
217
+ with open(f"{directory}/control.in", mode="w") as file:
218
+ file.write(f"#{'=' * 72}\n")
219
+ file.write(f"# FHI-aims geometry file: {directory}/geometry.in\n")
220
+ file.write("# File generated from pyaims\n")
221
+ file.write(f"# {time.asctime()}\n")
222
+ file.write(f"#{'=' * 72}\n")
223
+
224
+ file.write(content)
225
+
226
+ def as_dict(self) -> dict[str, Any]:
227
+ """Get a dictionary representation of the geometry.in file."""
228
+ dct: dict[str, Any] = {
229
+ "@module": type(self).__module__,
230
+ "@class": type(self).__name__,
231
+ "parameters": self.parameters,
232
+ "outputs": self.outputs
233
+ }
234
+ return dct
235
+
236
+ @classmethod
237
+ def from_file(cls, control_file: str | Path) -> "AimsControlIn":
238
+ """Instantiate the Control object """
239
+ with open(control_file, "r") as f:
240
+ lines = f.read()
241
+ parameters = {}
242
+ outputs = []
243
+
244
+ # get species' defaults from the file first
245
+ species = {}
246
+ species_re = re.compile(r"(?<=\n) *species.*?(?=\n *species|$)", re.DOTALL)
247
+ species_lines = re.findall(species_re, lines)
248
+ element_re = re.compile(r" *species *(\S+)", re.DOTALL)
249
+
250
+ for block in species_lines:
251
+ if re.match(element_re, block) is not None:
252
+ element = re.match(element_re, block).group(1)
253
+ species[element] = SpeciesDefaults.from_strings(block.split("\n"))
254
+
255
+ # then everything else
256
+ for line in lines:
257
+ # remove comments and blank lines
258
+ line = line[:line.find("#")].strip()
259
+ if not line:
260
+ continue
261
+ # stop at species
262
+ if line.startswith("species "):
263
+ break
264
+ k, v = line.split(maxsplit=1)
265
+ if k == "output":
266
+ if "cube" in v:
267
+ # TODO: does not work with Cubes yet
268
+ raise PyaimsError("Reading control.in from file does not work with cubes yet")
269
+ outputs.append(v)
270
+ else:
271
+ parameters[k] = v
272
+ return AimsControlIn(
273
+ parameters=parameters,
274
+ outputs=outputs,
275
+ species_defaults=species
276
+ )
277
+
278
+ @classmethod
279
+ def from_dict(cls, dct: dict[str, Any]) -> Self:
280
+ """Initialize from dictionary.
281
+
282
+ Args:
283
+ dct (dict[str, Any]): The MontyEncoded dictionary
284
+
285
+ Returns:
286
+ The AimsControlIn for dct
287
+ """
288
+ decoded = {key: MontyDecoder().process_decoded(val) for key, val in dct.items() if not key.startswith("@")}
289
+
290
+ return cls(parameters=decoded["parameters"], outputs=decoded["outputs"])
291
+