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.
- pyfhiaims-0.0.3/PKG-INFO +82 -0
- pyfhiaims-0.0.3/README.md +56 -0
- pyfhiaims-0.0.3/pyfhiaims/__init__.py +5 -0
- pyfhiaims-0.0.3/pyfhiaims/aims_input.py +49 -0
- pyfhiaims-0.0.3/pyfhiaims/calculation.py +15 -0
- pyfhiaims-0.0.3/pyfhiaims/cli/__init__.py +0 -0
- pyfhiaims-0.0.3/pyfhiaims/control/__init__.py +1 -0
- pyfhiaims-0.0.3/pyfhiaims/control/chunk.py +31 -0
- pyfhiaims-0.0.3/pyfhiaims/control/control.py +291 -0
- pyfhiaims-0.0.3/pyfhiaims/control/cube.py +210 -0
- pyfhiaims-0.0.3/pyfhiaims/control/kpoints.py +16 -0
- pyfhiaims-0.0.3/pyfhiaims/errors.py +9 -0
- pyfhiaims-0.0.3/pyfhiaims/geometry/__init__.py +1 -0
- pyfhiaims-0.0.3/pyfhiaims/geometry/atom.py +264 -0
- pyfhiaims-0.0.3/pyfhiaims/geometry/geometry.py +602 -0
- pyfhiaims-0.0.3/pyfhiaims/output_parser/__init__.py +0 -0
- pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_out_calculation_section.py +630 -0
- pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_out_header_section.py +335 -0
- pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_out_section.py +108 -0
- pyfhiaims-0.0.3/pyfhiaims/output_parser/aims_outputs.py +165 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/__init__.py +0 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/band_structure.py +13 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/parser/__init__.py +1 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/parser/abc.py +370 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/parser/converters.py +104 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/parser/parsers.py +435 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/parser/utils.py +36 -0
- pyfhiaims-0.0.3/pyfhiaims/outputs/stdout.py +75 -0
- pyfhiaims-0.0.3/pyfhiaims/species_defaults/__init__.py +0 -0
- pyfhiaims-0.0.3/pyfhiaims/species_defaults/basis_function.py +247 -0
- pyfhiaims-0.0.3/pyfhiaims/species_defaults/basis_set.py +275 -0
- pyfhiaims-0.0.3/pyfhiaims/species_defaults/electronic_configuration.py +69 -0
- pyfhiaims-0.0.3/pyfhiaims/species_defaults/integration_grid.py +154 -0
- pyfhiaims-0.0.3/pyfhiaims/species_defaults/species.py +360 -0
- pyfhiaims-0.0.3/pyfhiaims/utils/__init__.py +13 -0
- pyfhiaims-0.0.3/pyfhiaims/utils/typecast.py +21 -0
- pyfhiaims-0.0.3/pyfhiaims.egg-info/PKG-INFO +82 -0
- pyfhiaims-0.0.3/pyfhiaims.egg-info/SOURCES.txt +54 -0
- pyfhiaims-0.0.3/pyfhiaims.egg-info/dependency_links.txt +1 -0
- pyfhiaims-0.0.3/pyfhiaims.egg-info/requires.txt +13 -0
- pyfhiaims-0.0.3/pyfhiaims.egg-info/top_level.txt +3 -0
- pyfhiaims-0.0.3/pyproject.toml +168 -0
- pyfhiaims-0.0.3/setup.cfg +4 -0
- pyfhiaims-0.0.3/tests/__init__.py +5 -0
- pyfhiaims-0.0.3/tests/conftest.py +9 -0
- pyfhiaims-0.0.3/tests/test_basis_function.py +52 -0
- pyfhiaims-0.0.3/tests/test_basis_set.py +37 -0
- pyfhiaims-0.0.3/tests/test_electronic_configuration.py +31 -0
- pyfhiaims-0.0.3/tests/test_inputs/test_control.py +12 -0
- pyfhiaims-0.0.3/tests/test_inputs/test_geometry.py +291 -0
- pyfhiaims-0.0.3/tests/test_integration_grid.py +24 -0
- pyfhiaims-0.0.3/tests/test_parsers/test_parser.py +27 -0
- pyfhiaims-0.0.3/tests/test_parsers/test_sections.py +543 -0
- pyfhiaims-0.0.3/tests/test_stdout.py +22 -0
- pyfhiaims-0.0.3/tests/test_typecast.py +21 -0
- pyfhiaims-0.0.3/tests/utils.py +77 -0
pyfhiaims-0.0.3/PKG-INFO
ADDED
|
@@ -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,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
|
+
|