rsspolymlp 0.3.0__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 (74) hide show
  1. rsspolymlp-0.3.0/LICENSE +21 -0
  2. rsspolymlp-0.3.0/PKG-INFO +106 -0
  3. rsspolymlp-0.3.0/README.md +75 -0
  4. rsspolymlp-0.3.0/pyproject.toml +51 -0
  5. rsspolymlp-0.3.0/setup.cfg +4 -0
  6. rsspolymlp-0.3.0/src/rsspolymlp/__init__.py +1 -0
  7. rsspolymlp-0.3.0/src/rsspolymlp/analysis/__init__.py +1 -0
  8. rsspolymlp-0.3.0/src/rsspolymlp/analysis/eos.py +238 -0
  9. rsspolymlp-0.3.0/src/rsspolymlp/analysis/ghost_minima.py +247 -0
  10. rsspolymlp-0.3.0/src/rsspolymlp/analysis/load_plot_data.py +36 -0
  11. rsspolymlp-0.3.0/src/rsspolymlp/analysis/phase_analysis.py +340 -0
  12. rsspolymlp-0.3.0/src/rsspolymlp/analysis/rss_summarize.py +458 -0
  13. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/__init__.py +1 -0
  14. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/chiral_spg.py +85 -0
  15. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/deprecated/invert_and_swap.py +106 -0
  16. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/deprecated/irrep_position.py +379 -0
  17. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/deprecated/niggli.py +10 -0
  18. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/deprecated/struct_match.py +264 -0
  19. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/deprecated/utils.py +83 -0
  20. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/invert_and_swap.py +457 -0
  21. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/reduced_position.py +433 -0
  22. rsspolymlp-0.3.0/src/rsspolymlp/analysis/struct_matcher/struct_match.py +294 -0
  23. rsspolymlp-0.3.0/src/rsspolymlp/analysis/unique_struct.py +478 -0
  24. rsspolymlp-0.3.0/src/rsspolymlp/api/__init__.py +1 -0
  25. rsspolymlp-0.3.0/src/rsspolymlp/api/cli_rsspolymlp.py +338 -0
  26. rsspolymlp-0.3.0/src/rsspolymlp/api/cli_rsspolymlp_devkit.py +321 -0
  27. rsspolymlp-0.3.0/src/rsspolymlp/api/cli_rsspolymlp_plot.py +127 -0
  28. rsspolymlp-0.3.0/src/rsspolymlp/api/cli_rsspolymlp_utils.py +58 -0
  29. rsspolymlp-0.3.0/src/rsspolymlp/api/rsspolymlp.py +250 -0
  30. rsspolymlp-0.3.0/src/rsspolymlp/api/rsspolymlp_devkit.py +237 -0
  31. rsspolymlp-0.3.0/src/rsspolymlp/api/rsspolymlp_plot.py +363 -0
  32. rsspolymlp-0.3.0/src/rsspolymlp/api/rsspolymlp_utils.py +46 -0
  33. rsspolymlp-0.3.0/src/rsspolymlp/common/__init__.py +1 -0
  34. rsspolymlp-0.3.0/src/rsspolymlp/common/atomic_energy.py +38 -0
  35. rsspolymlp-0.3.0/src/rsspolymlp/common/composition.py +65 -0
  36. rsspolymlp-0.3.0/src/rsspolymlp/common/convert_dict.py +60 -0
  37. rsspolymlp-0.3.0/src/rsspolymlp/common/property.py +116 -0
  38. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/dataset/compress_vasprun.py +77 -0
  39. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/dataset/divide_dataset.py +130 -0
  40. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/dataset/gen_mlp_dataset.py +77 -0
  41. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/estimate_cost.py +57 -0
  42. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/model_selection/gen_hybrid_model.py +139 -0
  43. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/model_selection/gen_single_model.py +74 -0
  44. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/model_selection/n_feature.py +57 -0
  45. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/model_selection/pypolymlp_gridsearch.py +385 -0
  46. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/model_selection/reduce_grid.py +25 -0
  47. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/pareto_opt_mlp.py +214 -0
  48. rsspolymlp-0.3.0/src/rsspolymlp/mlp_dev/polymlp_dev.py +125 -0
  49. rsspolymlp-0.3.0/src/rsspolymlp/rss/__init__.py +1 -0
  50. rsspolymlp-0.3.0/src/rsspolymlp/rss/eliminate_duplicates.py +345 -0
  51. rsspolymlp-0.3.0/src/rsspolymlp/rss/load_logfile.py +176 -0
  52. rsspolymlp-0.3.0/src/rsspolymlp/rss/optimization_mlp.py +325 -0
  53. rsspolymlp-0.3.0/src/rsspolymlp/rss/random_struct.py +135 -0
  54. rsspolymlp-0.3.0/src/rsspolymlp/utils/__init__.py +1 -0
  55. rsspolymlp-0.3.0/src/rsspolymlp/utils/lammps_utils.py +240 -0
  56. rsspolymlp-0.3.0/src/rsspolymlp/utils/matplot_util/__init__.py +1 -0
  57. rsspolymlp-0.3.0/src/rsspolymlp/utils/matplot_util/custom_plt.py +103 -0
  58. rsspolymlp-0.3.0/src/rsspolymlp/utils/matplot_util/examples/__init__.py +1 -0
  59. rsspolymlp-0.3.0/src/rsspolymlp/utils/matplot_util/examples/example.py +122 -0
  60. rsspolymlp-0.3.0/src/rsspolymlp/utils/matplot_util/examples/template.py +72 -0
  61. rsspolymlp-0.3.0/src/rsspolymlp/utils/matplot_util/make_plot.py +349 -0
  62. rsspolymlp-0.3.0/src/rsspolymlp/utils/pymatgen_utils.py +165 -0
  63. rsspolymlp-0.3.0/src/rsspolymlp/utils/spglib_utils.py +232 -0
  64. rsspolymlp-0.3.0/src/rsspolymlp/utils/vasp_util/__init__.py +1 -0
  65. rsspolymlp-0.3.0/src/rsspolymlp/utils/vasp_util/api.py +154 -0
  66. rsspolymlp-0.3.0/src/rsspolymlp/utils/vasp_util/gen_incar.py +164 -0
  67. rsspolymlp-0.3.0/src/rsspolymlp/utils/vasp_util/gen_script.py +208 -0
  68. rsspolymlp-0.3.0/src/rsspolymlp/utils/vasp_util/write_poscar.py +38 -0
  69. rsspolymlp-0.3.0/src/rsspolymlp.egg-info/PKG-INFO +106 -0
  70. rsspolymlp-0.3.0/src/rsspolymlp.egg-info/SOURCES.txt +72 -0
  71. rsspolymlp-0.3.0/src/rsspolymlp.egg-info/dependency_links.txt +1 -0
  72. rsspolymlp-0.3.0/src/rsspolymlp.egg-info/entry_points.txt +5 -0
  73. rsspolymlp-0.3.0/src/rsspolymlp.egg-info/requires.txt +21 -0
  74. rsspolymlp-0.3.0/src/rsspolymlp.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hayato Wakai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: rsspolymlp
3
+ Version: 0.3.0
4
+ Summary: A framework for random structure search using polynomial MLPs
5
+ Author-email: Hayato Wakai <wakai@cms.mtl.kyoto-u.ac.jp>
6
+ Maintainer-email: Hayato Wakai <wakai@cms.mtl.kyoto-u.ac.jp>
7
+ License-Expression: MIT
8
+ Project-URL: homepage, https://github.com/hytwakai/rsspolymlp
9
+ Project-URL: repository, https://github.com/hytwakai/rsspolymlp
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: numpy
14
+ Requires-Dist: scipy
15
+ Requires-Dist: scikit-learn
16
+ Requires-Dist: joblib
17
+ Requires-Dist: pypolymlp
18
+ Requires-Dist: spglib
19
+ Requires-Dist: symfc
20
+ Provides-Extra: pymatgen
21
+ Requires-Dist: pymatgen; extra == "pymatgen"
22
+ Provides-Extra: matplotlib
23
+ Requires-Dist: matplotlib; extra == "matplotlib"
24
+ Provides-Extra: seaborn
25
+ Requires-Dist: seaborn; extra == "seaborn"
26
+ Provides-Extra: tools
27
+ Requires-Dist: pymatgen; extra == "tools"
28
+ Requires-Dist: matplotlib; extra == "tools"
29
+ Requires-Dist: seaborn; extra == "tools"
30
+ Dynamic: license-file
31
+
32
+ # A framework for random structure search (RSS) using polynomial MLPs
33
+
34
+ ## Citation of rsspolymlp
35
+
36
+ If you use `rsspolymlp` in your study, please cite the following articles.
37
+
38
+ “Efficient global crystal structure prediction using polynomial machine learning potential in the binary Al–Cu alloy system”, [J. Ceram. Soc. Jpn. 131, 762 (2023)](https://www.jstage.jst.go.jp/article/jcersj2/131/10/131_23053/_article/-char/ja/)
39
+ ```
40
+ @article{HayatoWakai202323053,
41
+ title="{Efficient global crystal structure prediction using polynomial machine learning potential in the binary Al–Cu alloy system}",
42
+ author={Hayato Wakai and Atsuto Seko and Isao Tanaka},
43
+ journal={J. Ceram. Soc. Jpn.},
44
+ volume={131},
45
+ number={10},
46
+ pages={762-766},
47
+ year={2023},
48
+ doi={10.2109/jcersj2.23053}
49
+ }
50
+ ```
51
+
52
+ ## Installation
53
+
54
+ ### Required libraries and python modules
55
+
56
+ - python >= 3.10
57
+ - scikit-learn
58
+ - joblib
59
+ - pypolymlp
60
+ - spglib
61
+ - symfc
62
+
63
+ [Optional]
64
+ - matplotlib (if plotting RSS results)
65
+ - seaborn (if plotting RSS results)
66
+
67
+ ### How to install
68
+ - Install from conda-forge
69
+
70
+ | Name | Downloads | Version | Platforms |
71
+ | --- | --- | --- | --- |
72
+ | [![Conda Recipe](https://img.shields.io/badge/recipe-rsspolymlp-green.svg)](https://anaconda.org/conda-forge/rsspolymlp) | [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/rsspolymlp.svg)](https://anaconda.org/conda-forge/rsspolymlp) | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/rsspolymlp.svg)](https://anaconda.org/conda-forge/rsspolymlp) | [![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/rsspolymlp.svg)](https://anaconda.org/conda-forge/rsspolymlp) |
73
+
74
+ ```shell
75
+ conda create -n rsspolymlp
76
+ conda activate rsspolymlp
77
+ conda install -c conda-forge rsspolymlp
78
+ ```
79
+
80
+ - Install from PyPI
81
+ ```shell
82
+ conda create -n rsspolymlp
83
+ conda activate rsspolymlp
84
+ conda install -c conda-forge scikit-learn joblib pypolymlp spglib symfc
85
+ pip install rsspolymlp
86
+ ```
87
+
88
+ ## How to use rsspolymlp
89
+
90
+ - [Workflow of RSS with polynomial MLPs](docs/rsspolymlp.md)
91
+ - Initial structure generation
92
+ - Global RSS with polynomial MLPs
93
+ - Unique structure identification and RSS result summarization
94
+ - Ghost minimum structure elimination
95
+ - Phase stability analysis
96
+ - [Development kit for polynomial MLPs](docs/rsspolymlp_devkit.md)
97
+ - MLP dataset generation
98
+ - DFT dataset division
99
+ - Polynomial MLP development
100
+ - Pareto-optimal MLP selection
101
+ - Python API
102
+ - [RSS workflow](docs/api_rsspolymlp.md)
103
+ - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
104
+ - Single-point calculation
105
+ - Local geometry optimization
106
+ - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
@@ -0,0 +1,75 @@
1
+ # A framework for random structure search (RSS) using polynomial MLPs
2
+
3
+ ## Citation of rsspolymlp
4
+
5
+ If you use `rsspolymlp` in your study, please cite the following articles.
6
+
7
+ “Efficient global crystal structure prediction using polynomial machine learning potential in the binary Al–Cu alloy system”, [J. Ceram. Soc. Jpn. 131, 762 (2023)](https://www.jstage.jst.go.jp/article/jcersj2/131/10/131_23053/_article/-char/ja/)
8
+ ```
9
+ @article{HayatoWakai202323053,
10
+ title="{Efficient global crystal structure prediction using polynomial machine learning potential in the binary Al–Cu alloy system}",
11
+ author={Hayato Wakai and Atsuto Seko and Isao Tanaka},
12
+ journal={J. Ceram. Soc. Jpn.},
13
+ volume={131},
14
+ number={10},
15
+ pages={762-766},
16
+ year={2023},
17
+ doi={10.2109/jcersj2.23053}
18
+ }
19
+ ```
20
+
21
+ ## Installation
22
+
23
+ ### Required libraries and python modules
24
+
25
+ - python >= 3.10
26
+ - scikit-learn
27
+ - joblib
28
+ - pypolymlp
29
+ - spglib
30
+ - symfc
31
+
32
+ [Optional]
33
+ - matplotlib (if plotting RSS results)
34
+ - seaborn (if plotting RSS results)
35
+
36
+ ### How to install
37
+ - Install from conda-forge
38
+
39
+ | Name | Downloads | Version | Platforms |
40
+ | --- | --- | --- | --- |
41
+ | [![Conda Recipe](https://img.shields.io/badge/recipe-rsspolymlp-green.svg)](https://anaconda.org/conda-forge/rsspolymlp) | [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/rsspolymlp.svg)](https://anaconda.org/conda-forge/rsspolymlp) | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/rsspolymlp.svg)](https://anaconda.org/conda-forge/rsspolymlp) | [![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/rsspolymlp.svg)](https://anaconda.org/conda-forge/rsspolymlp) |
42
+
43
+ ```shell
44
+ conda create -n rsspolymlp
45
+ conda activate rsspolymlp
46
+ conda install -c conda-forge rsspolymlp
47
+ ```
48
+
49
+ - Install from PyPI
50
+ ```shell
51
+ conda create -n rsspolymlp
52
+ conda activate rsspolymlp
53
+ conda install -c conda-forge scikit-learn joblib pypolymlp spglib symfc
54
+ pip install rsspolymlp
55
+ ```
56
+
57
+ ## How to use rsspolymlp
58
+
59
+ - [Workflow of RSS with polynomial MLPs](docs/rsspolymlp.md)
60
+ - Initial structure generation
61
+ - Global RSS with polynomial MLPs
62
+ - Unique structure identification and RSS result summarization
63
+ - Ghost minimum structure elimination
64
+ - Phase stability analysis
65
+ - [Development kit for polynomial MLPs](docs/rsspolymlp_devkit.md)
66
+ - MLP dataset generation
67
+ - DFT dataset division
68
+ - Polynomial MLP development
69
+ - Pareto-optimal MLP selection
70
+ - Python API
71
+ - [RSS workflow](docs/api_rsspolymlp.md)
72
+ - [VASP calculation utilities](src/rsspolymlp/utils/vasp_util/readme.md)
73
+ - Single-point calculation
74
+ - Local geometry optimization
75
+ - [Matplotlib utilities](src/rsspolymlp/utils/matplot_util/readme.md)
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rsspolymlp"
7
+ version = "0.3.0"
8
+ description = "A framework for random structure search using polynomial MLPs"
9
+ license = "MIT"
10
+ authors = [
11
+ {name = "Hayato Wakai", email = "wakai@cms.mtl.kyoto-u.ac.jp"},
12
+ ]
13
+ maintainers = [
14
+ {name = "Hayato Wakai", email = "wakai@cms.mtl.kyoto-u.ac.jp"},
15
+ ]
16
+ readme = {file = "README.md", content-type = "text/markdown"}
17
+ requires-python = ">=3.10"
18
+ dependencies = [
19
+ "numpy",
20
+ "scipy",
21
+ "scikit-learn",
22
+ "joblib",
23
+ "pypolymlp",
24
+ "spglib",
25
+ "symfc",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ pymatgen = ["pymatgen"]
30
+ matplotlib = ["matplotlib"]
31
+ seaborn = ["seaborn"]
32
+ tools = ["pymatgen", "matplotlib", "seaborn"]
33
+
34
+ [project.urls]
35
+ homepage = "https://github.com/hytwakai/rsspolymlp"
36
+ repository = "https://github.com/hytwakai/rsspolymlp"
37
+
38
+ [project.scripts]
39
+ rsspolymlp = "rsspolymlp.api.cli_rsspolymlp:run"
40
+ rsspolymlp-devkit = "rsspolymlp.api.cli_rsspolymlp_devkit:run"
41
+ rsspolymlp-plot = "rsspolymlp.api.cli_rsspolymlp_plot:run"
42
+ rsspolymlp-utils = "rsspolymlp.api.cli_rsspolymlp_utils:run"
43
+
44
+ [tool.setuptools]
45
+ package-dir = {"" = "src"}
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+
50
+ [tool.setuptools.package-data]
51
+ rsspolymlp = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ # src/rss_polymlp/__init__.py
@@ -0,0 +1 @@
1
+ # src/rss_polymlp/analysis/__init__.py
@@ -0,0 +1,238 @@
1
+ import logging
2
+ import warnings
3
+
4
+ import numpy as np
5
+ import scipy
6
+ from scipy.interpolate import interp1d
7
+ from scipy.optimize import leastsq
8
+
9
+ from pypolymlp.core.units import EVtoGPa
10
+
11
+
12
+ def vinet(v, *p):
13
+ """Return Vinet EOS.
14
+ p[0] = E_0
15
+ p[1] = B_0
16
+ p[2] = B'_0
17
+ p[3] = V_0
18
+ """
19
+ x = (v / p[3]) ** (1.0 / 3)
20
+ xi = 3.0 / 2 * (p[2] - 1)
21
+ return p[0] + (
22
+ 9 * p[1] * p[3] / (xi**2) * (1 + (xi * (1 - x) - 1) * np.exp(xi * (1 - x)))
23
+ )
24
+
25
+
26
+ class EOSFit:
27
+
28
+ def __init__(
29
+ self,
30
+ energies: np.ndarray,
31
+ volumes: np.ndarray,
32
+ init_parameter: list[float] = None,
33
+ pressure: float = None,
34
+ volume_range: list[float] = None,
35
+ ):
36
+ """
37
+ Fit an equation of state (EOS) to energy-volume data and
38
+ enable Gibbs energy interpolation at arbitrary pressures.
39
+ """
40
+ sort_idx = np.argsort(-np.array(volumes))
41
+ self._energies = np.array(energies)[sort_idx]
42
+ self._volumes = np.array(volumes)[sort_idx]
43
+
44
+ self._eos = vinet
45
+ self.parameters = None
46
+ self._volume_range = (
47
+ volume_range
48
+ if volume_range is not None
49
+ else [np.min(self._volumes) * 0.99, np.max(self._volumes) * 1.01]
50
+ )
51
+
52
+ if init_parameter is None:
53
+ # Default initial parameters: [E0, B0, B0', V0]
54
+ self._init_parameter = [
55
+ energies[len(energies) // 2],
56
+ 0.6,
57
+ 4.0,
58
+ volumes[len(volumes) // 2],
59
+ ]
60
+ else:
61
+ self._init_parameter = init_parameter
62
+
63
+ if pressure is not None:
64
+ self._energies += pressure * self._volumes / EVtoGPa
65
+
66
+ self.fit()
67
+ self.interpolate_gibbs_from_pressure(self._volume_range)
68
+
69
+ def fit(self):
70
+ """Fit EOS parameters to energy-volume data using least squares."""
71
+ warnings.filterwarnings("error")
72
+
73
+ def residuals(p, eos, v, e):
74
+ return eos(v, *p) - e
75
+
76
+ try:
77
+ result = leastsq(
78
+ residuals,
79
+ self._init_parameter,
80
+ args=(self._eos, self._volumes, self._energies),
81
+ full_output=1,
82
+ )
83
+ except RuntimeError:
84
+ logging.exception("Fitting to EOS failed.")
85
+ raise
86
+ except (RuntimeWarning, scipy.optimize.optimize.OptimizeWarning):
87
+ logging.exception("Difficulty in fitting to EOS.")
88
+ raise
89
+ else:
90
+ self.parameters = result[0]
91
+
92
+ print(f"RMSE of EOS fit (energy): {self.rmse*1000:.6f} meV")
93
+
94
+ @property
95
+ def rmse(self) -> float:
96
+ """Return RMSE between fitted EOS and input energy data."""
97
+ if self.parameters is None:
98
+ raise ValueError("Fit must be performed before calculating RMSE.")
99
+ predicted = self._eos(self._volumes, *self.parameters)
100
+ error = predicted - self._energies
101
+ return np.sqrt(np.mean(error**2))
102
+
103
+ def interpolate_gibbs_from_pressure(
104
+ self, volume_range: list[float], n_grid: int = None
105
+ ):
106
+ """
107
+ Precompute interpolation function for G(P) using volume range.
108
+ Stores:
109
+ self.g_interp: interpolator
110
+ self.pressure_lb / pressure_ub: pressure bounds
111
+ """
112
+ if n_grid is None:
113
+ # Pressure grid is set with ~0.01 GPa resolution.
114
+ pressures, _ = self._get_pressure_and_gibbs_from_volumes(
115
+ [min(volume_range), max(volume_range)]
116
+ )
117
+ pressure_range = abs(pressures[1] - pressures[0])
118
+ n_grid = int(round(pressure_range * 100))
119
+
120
+ volumes = np.linspace(min(volume_range), max(volume_range), n_grid)
121
+ pressures, gibbs_energies = self._get_pressure_and_gibbs_from_volumes(volumes)
122
+
123
+ sort_idx = np.argsort(pressures)
124
+ sorted_pressures = pressures[sort_idx]
125
+ sorted_gibbs = gibbs_energies[sort_idx]
126
+
127
+ self.pressure_lb = sorted_pressures[0]
128
+ self.pressure_ub = sorted_pressures[-1]
129
+
130
+ self.g_interp = interp1d(
131
+ sorted_pressures,
132
+ sorted_gibbs,
133
+ kind="linear",
134
+ bounds_error=True,
135
+ )
136
+
137
+ def _get_pressure_and_gibbs_from_volumes(
138
+ self, volumes: np.ndarray, eps: float = 1e-4
139
+ ) -> tuple[np.ndarray, np.ndarray]:
140
+ """Calculate pressure and Gibbs energy for each volume using numerical derivatives."""
141
+ gibbs_list = []
142
+ pressure_list = []
143
+ energies = self.get_energy(volumes)
144
+
145
+ for vol, e in zip(volumes, energies):
146
+ e_forward, e_backward = self.get_energy([vol + eps, vol - eps])
147
+ dE_dV = (e_forward - e_backward) / (2 * eps)
148
+ pressure_eVA3 = -dE_dV
149
+ gibbs = e + pressure_eVA3 * vol
150
+ pressure_GPa = pressure_eVA3 * EVtoGPa
151
+ gibbs_list.append(gibbs)
152
+ pressure_list.append(pressure_GPa)
153
+
154
+ return np.array(pressure_list), np.array(gibbs_list)
155
+
156
+ def get_energy(self, volumes: np.ndarray) -> np.ndarray:
157
+ """Return predicted energy values for given volumes using fitted EOS."""
158
+ return vinet(np.array(volumes), *self.parameters)
159
+
160
+ def get_gibbs(self, pressures: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
161
+ """Return interpolated Gibbs free energy values at given pressures in GPa."""
162
+ pressures = np.asarray(pressures)
163
+ in_range_mask = (self.pressure_lb <= pressures) & (
164
+ pressures <= self.pressure_ub
165
+ )
166
+ if not np.any(in_range_mask):
167
+ raise ValueError(
168
+ "All provided pressures are outside the interpolation range "
169
+ f"[{self.pressure_lb:.3f}, {self.pressure_ub:.3f}] GPa."
170
+ )
171
+ valid_pressures = pressures[in_range_mask]
172
+ return valid_pressures, self.g_interp(valid_pressures)
173
+
174
+ def rmse_from_enthalpy_data(
175
+ self,
176
+ pressures: np.ndarray,
177
+ reference_gibbs: np.ndarray,
178
+ ) -> float:
179
+ """
180
+ Compute RMSE between interpolated Gibbs free energies and reference values."""
181
+ _, predicted_gibbs = self.get_gibbs(pressures)
182
+ error = np.array(predicted_gibbs) - np.array(reference_gibbs)
183
+ return np.sqrt(np.mean(error**2))
184
+
185
+ def eos_plot(self, fig_name="EV_plot.png"):
186
+ from rsspolymlp.utils.matplot_util.custom_plt import CustomPlt
187
+ from rsspolymlp.utils.matplot_util.make_plot import MakePlot
188
+
189
+ custom_template = CustomPlt(
190
+ label_size=8,
191
+ label_pad=3.0,
192
+ xtick_size=7,
193
+ ytick_size=7,
194
+ xtick_pad=3.0,
195
+ ytick_pad=3.0,
196
+ )
197
+ plt = custom_template.get_custom_plt()
198
+ plotter = MakePlot(
199
+ plt=plt,
200
+ column_size=0.85,
201
+ height_ratio=0.95,
202
+ )
203
+ plotter.initialize_ax()
204
+ plotter.set_visuality(n_color=4, n_line=4, n_marker=0, color_type="grad")
205
+
206
+ volumes = np.linspace(min(self._volume_range), max(self._volume_range), 200)
207
+ energies = self.get_energy(volumes)
208
+
209
+ plotter.ax_plot(
210
+ volumes,
211
+ energies,
212
+ plot_type="closed",
213
+ label=None,
214
+ plot_size=0.0,
215
+ line_size=0.7,
216
+ zorder=1,
217
+ )
218
+ plotter.ax_scatter(
219
+ self._volumes,
220
+ self._energies,
221
+ plot_type="open",
222
+ label=None,
223
+ plot_size=0.7,
224
+ zorder=2,
225
+ )
226
+
227
+ plotter.finalize_ax(
228
+ xlabel=r"Volume ($\rm{\AA}^3$/atom)",
229
+ ylabel="Energy (eV/atom)",
230
+ )
231
+ plt.tight_layout()
232
+ plt.savefig(
233
+ fig_name,
234
+ bbox_inches="tight",
235
+ pad_inches=0.01,
236
+ dpi=400,
237
+ )
238
+ plt.close()