MatRiFFlow 2.0.1__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.
- matrifflow-2.0.1/LICENSE +9 -0
- matrifflow-2.0.1/PKG-INFO +136 -0
- matrifflow-2.0.1/README.md +84 -0
- matrifflow-2.0.1/pyproject.toml +127 -0
- matrifflow-2.0.1/scripts/gorun-all-ready.sh +245 -0
- matrifflow-2.0.1/setup.cfg +4 -0
- matrifflow-2.0.1/src/IMDgroup/__init__.py +82 -0
- matrifflow-2.0.1/src/MatRiFFlow.egg-info/PKG-INFO +136 -0
- matrifflow-2.0.1/src/MatRiFFlow.egg-info/SOURCES.txt +99 -0
- matrifflow-2.0.1/src/MatRiFFlow.egg-info/dependency_links.txt +1 -0
- matrifflow-2.0.1/src/MatRiFFlow.egg-info/entry_points.txt +8 -0
- matrifflow-2.0.1/src/MatRiFFlow.egg-info/requires.txt +33 -0
- matrifflow-2.0.1/src/MatRiFFlow.egg-info/top_level.txt +2 -0
- matrifflow-2.0.1/src/matrifflow/__init__.py +39 -0
- matrifflow-2.0.1/src/matrifflow/common.py +150 -0
- matrifflow-2.0.1/src/matrifflow/gorun/__init__.py +36 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/__init__.py +32 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/_mace_train_masked.py +218 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/atat_local.py +156 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/base.py +128 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/gpu.py +205 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/mace.py +873 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/maps_adapter.py +177 -0
- matrifflow-2.0.1/src/matrifflow/gorun/adapters/vasp.py +386 -0
- matrifflow-2.0.1/src/matrifflow/gorun/cleanVASP.py +334 -0
- matrifflow-2.0.1/src/matrifflow/gorun/core/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/gorun/core/files.py +168 -0
- matrifflow-2.0.1/src/matrifflow/gorun/core/incar.py +334 -0
- matrifflow-2.0.1/src/matrifflow/gorun/core/pipeline.py +345 -0
- matrifflow-2.0.1/src/matrifflow/gorun/data/gorun-config.example.toml +230 -0
- matrifflow-2.0.1/src/matrifflow/gorun/gorun.py +744 -0
- matrifflow-2.0.1/src/matrifflow/gorun/gorun_atat_local.py +230 -0
- matrifflow-2.0.1/src/matrifflow/gorun/gorun_maps.py +42 -0
- matrifflow-2.0.1/src/matrifflow/gorun/sbatch.py +253 -0
- matrifflow-2.0.1/src/matrifflow/gorun/slurm.py +226 -0
- matrifflow-2.0.1/src/matrifflow/gorun/vasp_calculator.py +80 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/__init__.py +30 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf.py +281 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_analyze.py +392 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_create.py +185 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_derive.py +1609 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_diff.py +419 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_run.py +69 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_status.py +842 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/cli/mrf_visualize.py +1808 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/core/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/core/structure.py +870 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/diffusion/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/diffusion/neb.py +832 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/atat.py +131 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/__init__.py +30 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/diagnostics.py +224 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/functionals.yaml +48 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/incar_toml.py +250 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/inputs.py +228 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/outputs.py +803 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/sets.py +1092 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/io/vasp/vaspdir.py +1514 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/transformations/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/transformations/insert_molecule.py +661 -0
- matrifflow-2.0.1/src/matrifflow/pymatgen/transformations/symmetry_clone.py +309 -0
- matrifflow-2.0.1/src/matrifflow/utils/__init__.py +28 -0
- matrifflow-2.0.1/src/matrifflow/utils/mpl.py +106 -0
- matrifflow-2.0.1/tests/test_adapters_atat_local.py +197 -0
- matrifflow-2.0.1/tests/test_adapters_gpu.py +62 -0
- matrifflow-2.0.1/tests/test_adapters_mace.py +378 -0
- matrifflow-2.0.1/tests/test_adapters_maps.py +256 -0
- matrifflow-2.0.1/tests/test_adapters_vasp.py +494 -0
- matrifflow-2.0.1/tests/test_atat.py +135 -0
- matrifflow-2.0.1/tests/test_cleanvasp.py +585 -0
- matrifflow-2.0.1/tests/test_common.py +71 -0
- matrifflow-2.0.1/tests/test_core_files.py +158 -0
- matrifflow-2.0.1/tests/test_core_incar.py +384 -0
- matrifflow-2.0.1/tests/test_diagnostics.py +139 -0
- matrifflow-2.0.1/tests/test_docs.py +94 -0
- matrifflow-2.0.1/tests/test_gorun_atat_local.py +63 -0
- matrifflow-2.0.1/tests/test_gorun_cli.py +405 -0
- matrifflow-2.0.1/tests/test_gorun_maps.py +58 -0
- matrifflow-2.0.1/tests/test_incar_toml.py +213 -0
- matrifflow-2.0.1/tests/test_inputs.py +200 -0
- matrifflow-2.0.1/tests/test_insert_molecule.py +673 -0
- matrifflow-2.0.1/tests/test_integration.py +428 -0
- matrifflow-2.0.1/tests/test_mpl.py +81 -0
- matrifflow-2.0.1/tests/test_mrf.py +179 -0
- matrifflow-2.0.1/tests/test_mrf_analyze.py +622 -0
- matrifflow-2.0.1/tests/test_mrf_create.py +247 -0
- matrifflow-2.0.1/tests/test_mrf_derive.py +779 -0
- matrifflow-2.0.1/tests/test_mrf_diff.py +518 -0
- matrifflow-2.0.1/tests/test_mrf_run.py +84 -0
- matrifflow-2.0.1/tests/test_mrf_status.py +824 -0
- matrifflow-2.0.1/tests/test_outputs.py +302 -0
- matrifflow-2.0.1/tests/test_pipeline.py +369 -0
- matrifflow-2.0.1/tests/test_sbatch.py +290 -0
- matrifflow-2.0.1/tests/test_sets.py +1220 -0
- matrifflow-2.0.1/tests/test_slurm.py +287 -0
- matrifflow-2.0.1/tests/test_structure.py +700 -0
- matrifflow-2.0.1/tests/test_symmetry_clone.py +267 -0
- matrifflow-2.0.1/tests/test_vasp_calculator.py +54 -0
- matrifflow-2.0.1/tests/test_vaspdir.py +1021 -0
matrifflow-2.0.1/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Inverse Materials Design Group
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: MatRiFFlow
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: Pymatgen add-ons and cluster job-submission tools for the Inverse Materials Design group (formerly IMDgroup-pymatgen)
|
|
5
|
+
Author-email: Ihor Radchenko <yantar92@posteo.net>, "Oleksandr I. Malyi" <oleksandrmalyi@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: homepage, https://yantar92.github.io/MatRiFFlow/
|
|
8
|
+
Keywords: crystal,materials,science,structure,VASP,pymatgen,cluster,MACE,slurm,ase
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: System :: Clustering
|
|
19
|
+
Requires-Python: <3.14,>=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy>=1.26.4
|
|
23
|
+
Requires-Dist: pymatgen
|
|
24
|
+
Requires-Dist: alive_progress==3.3.0
|
|
25
|
+
Requires-Dist: ase>=3.23.0
|
|
26
|
+
Requires-Dist: termcolor
|
|
27
|
+
Requires-Dist: rich
|
|
28
|
+
Requires-Dist: pandas
|
|
29
|
+
Requires-Dist: python-dateutil
|
|
30
|
+
Requires-Dist: pytimeparse
|
|
31
|
+
Requires-Dist: scikit-learn
|
|
32
|
+
Requires-Dist: networkx
|
|
33
|
+
Requires-Dist: cachetools
|
|
34
|
+
Requires-Dist: duecredit>=0.11
|
|
35
|
+
Requires-Dist: lmdb
|
|
36
|
+
Requires-Dist: typing-extensions
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pyright; extra == "dev"
|
|
39
|
+
Provides-Extra: test
|
|
40
|
+
Requires-Dist: pytest; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
42
|
+
Provides-Extra: docs
|
|
43
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
44
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
45
|
+
Requires-Dist: autodocsumm>=0.2; extra == "docs"
|
|
46
|
+
Requires-Dist: myst-parser>=2.0; extra == "docs"
|
|
47
|
+
Requires-Dist: sphinxcontrib-mermaid>=0.7; extra == "docs"
|
|
48
|
+
Provides-Extra: mace
|
|
49
|
+
Requires-Dist: mace-torch; extra == "mace"
|
|
50
|
+
Requires-Dist: torch; extra == "mace"
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# MatRiFFlow (formerly IMDgroup-pymatgen)
|
|
56
|
+
|
|
57
|
+
Python and command-line tools for **iterative development of
|
|
58
|
+
computational workflows** for VASP atomistic simulations, built on top
|
|
59
|
+
of [pymatgen](https://pymatgen.org/).
|
|
60
|
+
|
|
61
|
+
Works with ordinary calculation directories produced manually, by scripts,
|
|
62
|
+
or by **any existing workflow tooling that writes standard VASP files**.
|
|
63
|
+
The independent commands do not require a workflow manager or prescribe
|
|
64
|
+
how jobs must be executed.
|
|
65
|
+
|
|
66
|
+
Also includes `gorun`, a cluster-agnostic job-submission script for VASP,
|
|
67
|
+
MACE fine-tuning, ATAT, and generic GPU jobs on Slurm clusters.
|
|
68
|
+
|
|
69
|
+
Documentation: <https://yantar92.github.io/MatRiFFlow/>
|
|
70
|
+
|
|
71
|
+

|
|
72
|
+
|
|
73
|
+
`mrf` can handle large collections of calculations (including
|
|
74
|
+
running) and detect anomalies or incomparable results. It
|
|
75
|
+
deliberately **does not automate fixing anomalies**, leaving it up to
|
|
76
|
+
the user to explore the problems and identify the solutions (with the
|
|
77
|
+
optional help of `mrf` tooling).
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Workflow manager compatibility
|
|
81
|
+
|
|
82
|
+
`mrf` operates directly on standard VASP directory trees without a
|
|
83
|
+
workflow manager-specific database or import. It can complement manual
|
|
84
|
+
calculations, custom scripts, and existing workflow managers.
|
|
85
|
+
|
|
86
|
+
`mrf analyze` groups runs by identical settings, `mrf diff` compares
|
|
87
|
+
INCAR settings and structures, and `mrf analyze` provides calculation
|
|
88
|
+
summaries.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## High-throughput optimization
|
|
92
|
+
|
|
93
|
+
`mrf` can handle directory trees containing **thousands of
|
|
94
|
+
calculations**. Parsing VASP outputs (XML, OUTCAR) is costly on
|
|
95
|
+
supercomputer file systems, so the tools cache parsed results on disk
|
|
96
|
+
(with invalidation if the original data changes).
|
|
97
|
+
|
|
98
|
+
See the [Python API](https://yantar92.github.io/MatRiFFlow/python_api.html) for `MRFVaspDir`, the class providing cached
|
|
99
|
+
directory inspection.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## Installation
|
|
103
|
+
|
|
104
|
+
git clone https://github.com/yantar92/MatRiFFlow
|
|
105
|
+
cd MatRiFFlow
|
|
106
|
+
pip install .
|
|
107
|
+
|
|
108
|
+
This installs the `mrf` and `gorun` commands.
|
|
109
|
+
|
|
110
|
+
Note: the `lmdb` dependency (on-disk result cache) links against the
|
|
111
|
+
native LMDB library. On some systems – especially HPC clusters – you
|
|
112
|
+
must provide it before `pip install .`, for example `module load lmdb`
|
|
113
|
+
or the system dev package (`apt install liblmdb-dev`,
|
|
114
|
+
`dnf install lmdb-devel`). If the build fails while compiling `lmdb`,
|
|
115
|
+
load the module and re-run `pip install .`.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## Next steps
|
|
119
|
+
|
|
120
|
+
- [Quickstart](https://yantar92.github.io/MatRiFFlow/quickstart.html): example graphite (mp-48) workflow
|
|
121
|
+
- [Why this exists](https://yantar92.github.io/MatRiFFlow/concepts.html): semi-automated workflow development
|
|
122
|
+
- [Command-line reference](https://yantar92.github.io/MatRiFFlow/cli.html) and [job submission](https://yantar92.github.io/MatRiFFlow/gorun.html)
|
|
123
|
+
- [Python API](https://yantar92.github.io/MatRiFFlow/python_api.html)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## Contributing
|
|
127
|
+
|
|
128
|
+
Contributions are welcome.
|
|
129
|
+
|
|
130
|
+
- Report bugs and propose features by opening an [issue](https://github.com/yantar92/MatRiFFlow/issues).
|
|
131
|
+
- Ask questions and discuss design in [GitHub Discussions](https://github.com/yantar92/MatRiFFlow/discussions).
|
|
132
|
+
- Submit changes as [pull requests](https://github.com/yantar92/MatRiFFlow/pulls).
|
|
133
|
+
|
|
134
|
+
You can also contact the maintainer directly by email
|
|
135
|
+
(`yantar92@posteo.net`) or on GitHub.
|
|
136
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
# MatRiFFlow (formerly IMDgroup-pymatgen)
|
|
4
|
+
|
|
5
|
+
Python and command-line tools for **iterative development of
|
|
6
|
+
computational workflows** for VASP atomistic simulations, built on top
|
|
7
|
+
of [pymatgen](https://pymatgen.org/).
|
|
8
|
+
|
|
9
|
+
Works with ordinary calculation directories produced manually, by scripts,
|
|
10
|
+
or by **any existing workflow tooling that writes standard VASP files**.
|
|
11
|
+
The independent commands do not require a workflow manager or prescribe
|
|
12
|
+
how jobs must be executed.
|
|
13
|
+
|
|
14
|
+
Also includes `gorun`, a cluster-agnostic job-submission script for VASP,
|
|
15
|
+
MACE fine-tuning, ATAT, and generic GPU jobs on Slurm clusters.
|
|
16
|
+
|
|
17
|
+
Documentation: <https://yantar92.github.io/MatRiFFlow/>
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
`mrf` can handle large collections of calculations (including
|
|
22
|
+
running) and detect anomalies or incomparable results. It
|
|
23
|
+
deliberately **does not automate fixing anomalies**, leaving it up to
|
|
24
|
+
the user to explore the problems and identify the solutions (with the
|
|
25
|
+
optional help of `mrf` tooling).
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Workflow manager compatibility
|
|
29
|
+
|
|
30
|
+
`mrf` operates directly on standard VASP directory trees without a
|
|
31
|
+
workflow manager-specific database or import. It can complement manual
|
|
32
|
+
calculations, custom scripts, and existing workflow managers.
|
|
33
|
+
|
|
34
|
+
`mrf analyze` groups runs by identical settings, `mrf diff` compares
|
|
35
|
+
INCAR settings and structures, and `mrf analyze` provides calculation
|
|
36
|
+
summaries.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## High-throughput optimization
|
|
40
|
+
|
|
41
|
+
`mrf` can handle directory trees containing **thousands of
|
|
42
|
+
calculations**. Parsing VASP outputs (XML, OUTCAR) is costly on
|
|
43
|
+
supercomputer file systems, so the tools cache parsed results on disk
|
|
44
|
+
(with invalidation if the original data changes).
|
|
45
|
+
|
|
46
|
+
See the [Python API](https://yantar92.github.io/MatRiFFlow/python_api.html) for `MRFVaspDir`, the class providing cached
|
|
47
|
+
directory inspection.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
git clone https://github.com/yantar92/MatRiFFlow
|
|
53
|
+
cd MatRiFFlow
|
|
54
|
+
pip install .
|
|
55
|
+
|
|
56
|
+
This installs the `mrf` and `gorun` commands.
|
|
57
|
+
|
|
58
|
+
Note: the `lmdb` dependency (on-disk result cache) links against the
|
|
59
|
+
native LMDB library. On some systems – especially HPC clusters – you
|
|
60
|
+
must provide it before `pip install .`, for example `module load lmdb`
|
|
61
|
+
or the system dev package (`apt install liblmdb-dev`,
|
|
62
|
+
`dnf install lmdb-devel`). If the build fails while compiling `lmdb`,
|
|
63
|
+
load the module and re-run `pip install .`.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Next steps
|
|
67
|
+
|
|
68
|
+
- [Quickstart](https://yantar92.github.io/MatRiFFlow/quickstart.html): example graphite (mp-48) workflow
|
|
69
|
+
- [Why this exists](https://yantar92.github.io/MatRiFFlow/concepts.html): semi-automated workflow development
|
|
70
|
+
- [Command-line reference](https://yantar92.github.io/MatRiFFlow/cli.html) and [job submission](https://yantar92.github.io/MatRiFFlow/gorun.html)
|
|
71
|
+
- [Python API](https://yantar92.github.io/MatRiFFlow/python_api.html)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Contributing
|
|
75
|
+
|
|
76
|
+
Contributions are welcome.
|
|
77
|
+
|
|
78
|
+
- Report bugs and propose features by opening an [issue](https://github.com/yantar92/MatRiFFlow/issues).
|
|
79
|
+
- Ask questions and discuss design in [GitHub Discussions](https://github.com/yantar92/MatRiFFlow/discussions).
|
|
80
|
+
- Submit changes as [pull requests](https://github.com/yantar92/MatRiFFlow/pulls).
|
|
81
|
+
|
|
82
|
+
You can also contact the maintainer directly by email
|
|
83
|
+
(`yantar92@posteo.net`) or on GitHub.
|
|
84
|
+
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "MatRiFFlow"
|
|
7
|
+
version = "2.0.1"
|
|
8
|
+
description = "Pymatgen add-ons and cluster job-submission tools for the Inverse Materials Design group (formerly IMDgroup-pymatgen)"
|
|
9
|
+
authors = [ {name = "Ihor Radchenko", email = "yantar92@posteo.net"},
|
|
10
|
+
{name = "Oleksandr I. Malyi", email = "oleksandrmalyi@gmail.com"} ]
|
|
11
|
+
license = "MIT"
|
|
12
|
+
readme = {file = "README.md", content-type = "text/markdown"}
|
|
13
|
+
keywords = ["crystal", "materials", "science", "structure", "VASP", "pymatgen", "cluster", "MACE", "slurm", "ase"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Chemistry",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
"Topic :: System :: Clustering"]
|
|
25
|
+
requires-python = ">=3.12, <3.14"
|
|
26
|
+
dependencies = [
|
|
27
|
+
"numpy>=1.26.4",
|
|
28
|
+
"pymatgen",
|
|
29
|
+
"alive_progress==3.3.0",
|
|
30
|
+
"ase>=3.23.0",
|
|
31
|
+
"termcolor",
|
|
32
|
+
"rich",
|
|
33
|
+
"pandas",
|
|
34
|
+
"python-dateutil",
|
|
35
|
+
"pytimeparse",
|
|
36
|
+
"scikit-learn",
|
|
37
|
+
"networkx",
|
|
38
|
+
"cachetools",
|
|
39
|
+
"duecredit>=0.11",
|
|
40
|
+
"lmdb",
|
|
41
|
+
"typing-extensions"
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
homepage = "https://yantar92.github.io/MatRiFFlow/"
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
dev = ["pyright"]
|
|
49
|
+
test = ["pytest", "pytest-cov"]
|
|
50
|
+
docs = [
|
|
51
|
+
"sphinx>=7.0",
|
|
52
|
+
"sphinx-rtd-theme>=2.0",
|
|
53
|
+
"autodocsumm>=0.2",
|
|
54
|
+
"myst-parser>=2.0",
|
|
55
|
+
"sphinxcontrib-mermaid>=0.7",
|
|
56
|
+
]
|
|
57
|
+
mace = [
|
|
58
|
+
"mace-torch",
|
|
59
|
+
"torch",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[project.scripts]
|
|
63
|
+
mrf = "matrifflow.pymatgen.cli.mrf:main"
|
|
64
|
+
imdg = "matrifflow.pymatgen.cli.mrf:main_deprecated_imdg"
|
|
65
|
+
gorun = "matrifflow.gorun.gorun:main"
|
|
66
|
+
gorun-mace = "matrifflow.gorun.gorun:main_mace"
|
|
67
|
+
gorun-mace-finetune = "matrifflow.gorun.gorun:main_mace"
|
|
68
|
+
gorun-atat-local = "matrifflow.gorun.gorun_atat_local:main"
|
|
69
|
+
gorun-maps = "matrifflow.gorun.gorun_maps:main"
|
|
70
|
+
|
|
71
|
+
[tool.setuptools]
|
|
72
|
+
script-files = ["scripts/gorun-all-ready.sh"]
|
|
73
|
+
|
|
74
|
+
[tool.setuptools.packages.find]
|
|
75
|
+
exclude = ["tests"]
|
|
76
|
+
where = ["src/"]
|
|
77
|
+
include = [
|
|
78
|
+
"matrifflow.pymatgen",
|
|
79
|
+
"matrifflow.pymatgen.*",
|
|
80
|
+
"matrifflow",
|
|
81
|
+
"matrifflow.*",
|
|
82
|
+
"IMDgroup",
|
|
83
|
+
"IMDgroup.*",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[tool.setuptools.package-data]
|
|
87
|
+
"matrifflow.pymatgen.io.vasp" = ["*.cif", "*.yaml"]
|
|
88
|
+
"matrifflow.gorun" = ["data/*.toml"]
|
|
89
|
+
|
|
90
|
+
[tool.pylint]
|
|
91
|
+
source-roots=['./src']
|
|
92
|
+
|
|
93
|
+
[tool.pyright]
|
|
94
|
+
include = ["src"]
|
|
95
|
+
exclude = ["**/__pycache__", "build", "**/.mypy_cache"]
|
|
96
|
+
typeCheckingMode = "strict"
|
|
97
|
+
pythonPlatform = "Linux"
|
|
98
|
+
|
|
99
|
+
[tool.coverage.run]
|
|
100
|
+
branch = true
|
|
101
|
+
source = ["matrifflow"]
|
|
102
|
+
|
|
103
|
+
[tool.pytest.ini_options]
|
|
104
|
+
testpaths = ["tests"]
|
|
105
|
+
addopts = "-ra -m 'not heavy' --cov=matrifflow --cov-report=term-missing"
|
|
106
|
+
pythonpath = ["src"]
|
|
107
|
+
filterwarnings = [
|
|
108
|
+
# Upstream warnings we cannot fix; harmless in tests.
|
|
109
|
+
"ignore:Casting complex values to real discards the imaginary part:numpy.exceptions.ComplexWarning:pymatgen\\.core\\.operations",
|
|
110
|
+
# No module field on the next two filters. pytest.warns() re-emits
|
|
111
|
+
# every warning it did not match itself with module="warnings" (see
|
|
112
|
+
# _pytest/recwarn.py), so a module-scoped filter never matches a
|
|
113
|
+
# warning raised inside a pytest.warns() block. The POTCAR warning is
|
|
114
|
+
# also raised with stacklevel=2, which reports the caller's module
|
|
115
|
+
# instead of pymatgen.io.vasp.outputs.
|
|
116
|
+
"ignore:No POTCAR file with matching TITEL fields:UserWarning",
|
|
117
|
+
"ignore:Set OLD_ERROR_HANDLING to false and catch the errors directly:DeprecationWarning",
|
|
118
|
+
# Emitted outside pytest.warns() blocks, so these stay module-scoped.
|
|
119
|
+
"ignore:Setting the shape on a NumPy array has been deprecated:DeprecationWarning:ase\\.atoms",
|
|
120
|
+
# Our own warning, expected noise in integration tests: the fake VASP
|
|
121
|
+
# fixture has no vdw_kernel.bindat.
|
|
122
|
+
"ignore:Cannot find vdw_kernel.bindat:UserWarning:matrifflow\\.gorun\\.cleanVASP",
|
|
123
|
+
]
|
|
124
|
+
markers = [
|
|
125
|
+
"integration: tests that require generated VASP fixtures (see scripts/prepare_vasp_fixtures.sh)",
|
|
126
|
+
"heavy: slow tests, skipped by default (run with pytest -m heavy)",
|
|
127
|
+
]
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2024-2026 Inverse Materials Design Group
|
|
6
|
+
#
|
|
7
|
+
# Author: Ihor Radchenko <yantar92@posteo.net>
|
|
8
|
+
#
|
|
9
|
+
# This file is a part of MatRiFFlow package
|
|
10
|
+
#
|
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
# in the Software without restriction, including without limitation the rights
|
|
14
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
# furnished to do so, subject to the following conditions:
|
|
17
|
+
#
|
|
18
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
# copies or substantial portions of the Software.
|
|
20
|
+
#
|
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
# SOFTWARE.
|
|
28
|
+
|
|
29
|
+
# gorun-all-ready.sh - Submit pending VASP jobs marked with 'gorun_ready' files
|
|
30
|
+
#
|
|
31
|
+
# This script searches for directories containing a 'gorun_ready' file,
|
|
32
|
+
# waits until the user's Slurm job count drops below a limit, then submits
|
|
33
|
+
# the job with 'sbatch sub' and removes the marker file.
|
|
34
|
+
#
|
|
35
|
+
# Usage: gorun-all-ready.sh [OPTIONS]
|
|
36
|
+
#
|
|
37
|
+
# Options:
|
|
38
|
+
# -n, --max-jobs NUM Maximum concurrent Slurm jobs (default: 100)
|
|
39
|
+
# -d, --directory DIR Search root directory (default: current directory)
|
|
40
|
+
# -t, --timeout SEC Sleep interval between queue checks (default: 5)
|
|
41
|
+
# -q, --quiet Do not print extra information
|
|
42
|
+
# -h, --help Show this help text
|
|
43
|
+
#
|
|
44
|
+
# Example:
|
|
45
|
+
# gorun-all-ready.sh -n 20 -d /path/to/projects
|
|
46
|
+
#
|
|
47
|
+
# Exit codes:
|
|
48
|
+
# 0 Success
|
|
49
|
+
# 1 Argument error
|
|
50
|
+
# 2 Missing required command (squeue, sbatch, find)
|
|
51
|
+
# 3 No 'gorun_ready' files found
|
|
52
|
+
# 4 Sub‑mission failure (sbatch returned non‑zero)
|
|
53
|
+
#
|
|
54
|
+
# The script requires a working Slurm environment and the 'sub' script
|
|
55
|
+
# in each target directory.
|
|
56
|
+
|
|
57
|
+
set -euo pipefail
|
|
58
|
+
|
|
59
|
+
# ------------------------------------------------------------
|
|
60
|
+
# Configuration and defaults
|
|
61
|
+
# ------------------------------------------------------------
|
|
62
|
+
readonly PROGNAME="${0##*/}"
|
|
63
|
+
readonly VERSION="1.0.0"
|
|
64
|
+
|
|
65
|
+
MAX_JOBS=100
|
|
66
|
+
SEARCH_DIR="."
|
|
67
|
+
SLEEP_INTERVAL=5
|
|
68
|
+
VERBOSE=true
|
|
69
|
+
|
|
70
|
+
# ------------------------------------------------------------
|
|
71
|
+
# Utility functions
|
|
72
|
+
# ------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
err() {
|
|
75
|
+
printf "%s: %s\n" "$PROGNAME" "$*" >&2
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
log() {
|
|
79
|
+
if "$VERBOSE"; then
|
|
80
|
+
printf "%s: %s\n" "$PROGNAME" "$*"
|
|
81
|
+
fi
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
die() {
|
|
85
|
+
err "$@"
|
|
86
|
+
exit 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
usage() {
|
|
90
|
+
cat <<EOF
|
|
91
|
+
$PROGNAME - Submit pending VASP jobs marked with 'gorun_ready' files
|
|
92
|
+
|
|
93
|
+
Usage: $PROGNAME [OPTIONS]
|
|
94
|
+
|
|
95
|
+
Options:
|
|
96
|
+
-n, --max-jobs NUM Maximum concurrent Slurm jobs (default: 100)
|
|
97
|
+
-d, --directory DIR Search root directory (default: current directory)
|
|
98
|
+
-t, --timeout SEC Sleep interval between queue checks (default: 5)
|
|
99
|
+
-q, --quiet Do not print extra information
|
|
100
|
+
-h, --help Show this help text
|
|
101
|
+
--version Show version information
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
$PROGNAME -n 20 -d /path/to/projects
|
|
105
|
+
|
|
106
|
+
Exit codes:
|
|
107
|
+
0 Success
|
|
108
|
+
1 Argument error
|
|
109
|
+
2 Missing required command
|
|
110
|
+
3 No 'gorun_ready' files found
|
|
111
|
+
4 Submission failure
|
|
112
|
+
EOF
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
version() {
|
|
116
|
+
echo "$PROGNAME $VERSION"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Check that required commands are available
|
|
120
|
+
check_commands() {
|
|
121
|
+
local cmd
|
|
122
|
+
for cmd in squeue sbatch find; do
|
|
123
|
+
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
124
|
+
die "Required command '$cmd' not found in PATH"
|
|
125
|
+
fi
|
|
126
|
+
done
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Count current Slurm jobs for the current user
|
|
130
|
+
# Output: number of jobs (integer)
|
|
131
|
+
get_n_jobs() {
|
|
132
|
+
# Use -o %Z to get job state column; tail -n +2 skips header line
|
|
133
|
+
# The count includes pending and running jobs.
|
|
134
|
+
local count
|
|
135
|
+
if ! count=$(squeue -u "$USER" -o %Z 2>/dev/null | tail -n +2 | wc -l); then
|
|
136
|
+
err "squeue failed; assuming 0 jobs"
|
|
137
|
+
echo 0
|
|
138
|
+
return 1
|
|
139
|
+
fi
|
|
140
|
+
echo "$count"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# ------------------------------------------------------------
|
|
144
|
+
# Argument parsing
|
|
145
|
+
# ------------------------------------------------------------
|
|
146
|
+
while [[ $# -gt 0 ]]; do
|
|
147
|
+
case "$1" in
|
|
148
|
+
-n|--max-jobs)
|
|
149
|
+
if [[ -z "${2:-}" ]]; then
|
|
150
|
+
die "Missing argument for $1"
|
|
151
|
+
fi
|
|
152
|
+
if [[ ! "$2" =~ ^[0-9]+$ ]]; then
|
|
153
|
+
die "Max jobs must be a positive integer"
|
|
154
|
+
fi
|
|
155
|
+
MAX_JOBS="$2"
|
|
156
|
+
shift 2
|
|
157
|
+
;;
|
|
158
|
+
-d|--directory)
|
|
159
|
+
if [[ -z "${2:-}" ]]; then
|
|
160
|
+
die "Missing argument for $1"
|
|
161
|
+
fi
|
|
162
|
+
if [[ ! -d "$2" ]]; then
|
|
163
|
+
die "Directory '$2' does not exist"
|
|
164
|
+
fi
|
|
165
|
+
SEARCH_DIR="$2"
|
|
166
|
+
shift 2
|
|
167
|
+
;;
|
|
168
|
+
-t|--timeout)
|
|
169
|
+
if [[ -z "${2:-}" ]]; then
|
|
170
|
+
die "Missing argument for $1"
|
|
171
|
+
fi
|
|
172
|
+
if [[ ! "$2" =~ ^[0-9]+$ ]]; then
|
|
173
|
+
die "Timeout must be a positive integer"
|
|
174
|
+
fi
|
|
175
|
+
SLEEP_INTERVAL="$2"
|
|
176
|
+
shift 2
|
|
177
|
+
;;
|
|
178
|
+
-q|--quiet)
|
|
179
|
+
VERBOSE=false
|
|
180
|
+
shift
|
|
181
|
+
;;
|
|
182
|
+
-h|--help)
|
|
183
|
+
usage
|
|
184
|
+
exit 0
|
|
185
|
+
;;
|
|
186
|
+
--version)
|
|
187
|
+
version
|
|
188
|
+
exit 0
|
|
189
|
+
;;
|
|
190
|
+
*)
|
|
191
|
+
die "Unknown option: $1"
|
|
192
|
+
;;
|
|
193
|
+
esac
|
|
194
|
+
done
|
|
195
|
+
|
|
196
|
+
# ------------------------------------------------------------
|
|
197
|
+
# Main
|
|
198
|
+
# ------------------------------------------------------------
|
|
199
|
+
check_commands
|
|
200
|
+
|
|
201
|
+
log "Starting with max_jobs=$MAX_JOBS, search_dir=$SEARCH_DIR, sleep=$SLEEP_INTERVAL"
|
|
202
|
+
|
|
203
|
+
# Collect directories containing gorun_ready
|
|
204
|
+
readarray -t ready_dirs < <(
|
|
205
|
+
find "$SEARCH_DIR" -type f -name 'gorun_ready' -printf '%h\n' | sort -u
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if [[ ${#ready_dirs[@]} -eq 0 ]]; then
|
|
209
|
+
log "No 'gorun_ready' files found in $SEARCH_DIR"
|
|
210
|
+
exit 3
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
log "Found ${#ready_dirs[@]} directories with gorun_ready"
|
|
214
|
+
|
|
215
|
+
for dir in "${ready_dirs[@]}"; do
|
|
216
|
+
log "Processing $dir"
|
|
217
|
+
|
|
218
|
+
# Wait until job count drops below limit
|
|
219
|
+
while [[ $(get_n_jobs) -ge "$MAX_JOBS" ]]; do
|
|
220
|
+
log "Job count $(get_n_jobs) >= $MAX_JOBS, sleeping $SLEEP_INTERVAL seconds"
|
|
221
|
+
sleep "$SLEEP_INTERVAL"
|
|
222
|
+
done
|
|
223
|
+
|
|
224
|
+
# Enter directory and submit
|
|
225
|
+
(
|
|
226
|
+
cd "$dir" || die "Cannot cd to $dir"
|
|
227
|
+
|
|
228
|
+
if [[ ! -f "sub" ]]; then
|
|
229
|
+
err "Missing 'sub' script in $dir; skipping"
|
|
230
|
+
exit 0
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
log "Submitting job in $dir"
|
|
234
|
+
if sbatch sub; then
|
|
235
|
+
rm -f gorun_ready
|
|
236
|
+
log "Submitted and removed gorun_ready in $dir"
|
|
237
|
+
else
|
|
238
|
+
err "sbatch failed in $dir; keeping gorun_ready"
|
|
239
|
+
exit 4
|
|
240
|
+
fi
|
|
241
|
+
)
|
|
242
|
+
done
|
|
243
|
+
|
|
244
|
+
log "All pending jobs submitted"
|
|
245
|
+
exit 0
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Deprecated compatibility package for the MatRiFFlow rename.
|
|
2
|
+
|
|
3
|
+
``IMDgroup`` was the import package name before the project was renamed to
|
|
4
|
+
MatRiFFlow (import package ``matrifflow``). Importing anything under
|
|
5
|
+
``IMDgroup`` emits a :class:`DeprecationWarning` and resolves to the
|
|
6
|
+
corresponding ``matrifflow`` module, so existing scripts keep working
|
|
7
|
+
during the transition.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import importlib
|
|
11
|
+
import importlib.abc
|
|
12
|
+
import importlib.util
|
|
13
|
+
import sys
|
|
14
|
+
import warnings
|
|
15
|
+
|
|
16
|
+
_LEGACY = "IMDgroup"
|
|
17
|
+
_TARGET = "matrifflow"
|
|
18
|
+
|
|
19
|
+
# The 8 CLI subcommand modules were renamed ``imdg*`` -> ``mrf*`` during the
|
|
20
|
+
# MatRiFFlow rename. The prefix-only mapping above cannot recover those, so
|
|
21
|
+
# map their basenames explicitly. Everything else shares its basename.
|
|
22
|
+
_CLI_REMAP = {
|
|
23
|
+
"imdg": "mrf",
|
|
24
|
+
"imdg_create": "mrf_create",
|
|
25
|
+
"imdg_derive": "mrf_derive",
|
|
26
|
+
"imdg_diff": "mrf_diff",
|
|
27
|
+
"imdg_analyze": "mrf_analyze",
|
|
28
|
+
"imdg_status": "mrf_status",
|
|
29
|
+
"imdg_visualize": "mrf_visualize",
|
|
30
|
+
"imdg_run": "mrf_run",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
warnings.warn(
|
|
34
|
+
"IMDgroup is deprecated; use matrifflow instead.",
|
|
35
|
+
DeprecationWarning,
|
|
36
|
+
stacklevel=2,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class _AliasLoader(importlib.abc.Loader):
|
|
41
|
+
"""Loader that returns the already-imported real module.
|
|
42
|
+
|
|
43
|
+
Reusing the real module object (rather than re-executing its source)
|
|
44
|
+
means ``IMDgroup.X`` *is* ``matrifflow.X``, so ``isinstance`` checks,
|
|
45
|
+
pickling, and pymatgen's MSONable registry keep working across the two
|
|
46
|
+
import paths.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, real_name: str):
|
|
50
|
+
self._real_name = real_name
|
|
51
|
+
|
|
52
|
+
def create_module(self, spec):
|
|
53
|
+
_ = spec # unused
|
|
54
|
+
return importlib.import_module(self._real_name)
|
|
55
|
+
|
|
56
|
+
def exec_module(self, module):
|
|
57
|
+
_ = module # unused
|
|
58
|
+
# Already executed by create_module()'s import_module() call.
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class _AliasFinder(importlib.abc.MetaPathFinder):
|
|
63
|
+
def find_spec(self, fullname, path=None, target=None):
|
|
64
|
+
_, _ = path, target # unused
|
|
65
|
+
if fullname == _LEGACY or not fullname.startswith(_LEGACY + "."):
|
|
66
|
+
return None
|
|
67
|
+
real_name = _TARGET + fullname[len(_LEGACY):]
|
|
68
|
+
pkg, _, leaf = real_name.rpartition(".")
|
|
69
|
+
if leaf in _CLI_REMAP:
|
|
70
|
+
real_name = f"{pkg}.{_CLI_REMAP[leaf]}"
|
|
71
|
+
real_spec = importlib.util.find_spec(real_name)
|
|
72
|
+
if real_spec is None:
|
|
73
|
+
return None
|
|
74
|
+
spec = importlib.util.spec_from_loader(fullname, _AliasLoader(real_name))
|
|
75
|
+
assert spec is not None
|
|
76
|
+
if real_spec.submodule_search_locations is not None:
|
|
77
|
+
spec.submodule_search_locations = list(real_spec.submodule_search_locations)
|
|
78
|
+
return spec
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if not any(isinstance(f, _AliasFinder) for f in sys.meta_path):
|
|
82
|
+
sys.meta_path.insert(0, _AliasFinder())
|