rsspolymlp 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. rsspolymlp-0.0.2/LICENSE +21 -0
  2. rsspolymlp-0.0.2/PKG-INFO +95 -0
  3. rsspolymlp-0.0.2/README.md +67 -0
  4. rsspolymlp-0.0.2/pyproject.toml +49 -0
  5. rsspolymlp-0.0.2/setup.cfg +4 -0
  6. rsspolymlp-0.0.2/src/rsspolymlp/__init__.py +1 -0
  7. rsspolymlp-0.0.2/src/rsspolymlp/analysis/__init__.py +1 -0
  8. rsspolymlp-0.0.2/src/rsspolymlp/analysis/convex_hull.py +117 -0
  9. rsspolymlp-0.0.2/src/rsspolymlp/analysis/outlier_cands.py +76 -0
  10. rsspolymlp-0.0.2/src/rsspolymlp/analysis/plot/binary.py +114 -0
  11. rsspolymlp-0.0.2/src/rsspolymlp/analysis/rss_summarize.py +225 -0
  12. rsspolymlp-0.0.2/src/rsspolymlp/analysis/struct_matcher/__init__.py +1 -0
  13. rsspolymlp-0.0.2/src/rsspolymlp/analysis/struct_matcher/chiral_spg.py +80 -0
  14. rsspolymlp-0.0.2/src/rsspolymlp/analysis/struct_matcher/invert_and_permute.py +84 -0
  15. rsspolymlp-0.0.2/src/rsspolymlp/analysis/struct_matcher/irrep_position.py +285 -0
  16. rsspolymlp-0.0.2/src/rsspolymlp/analysis/struct_matcher/struct_match.py +145 -0
  17. rsspolymlp-0.0.2/src/rsspolymlp/analysis/struct_matcher/utils.py +57 -0
  18. rsspolymlp-0.0.2/src/rsspolymlp/analysis/unique_struct.py +216 -0
  19. rsspolymlp-0.0.2/src/rsspolymlp/common/__init__.py +1 -0
  20. rsspolymlp-0.0.2/src/rsspolymlp/common/comp_ratio.py +65 -0
  21. rsspolymlp-0.0.2/src/rsspolymlp/common/parse_arg.py +143 -0
  22. rsspolymlp-0.0.2/src/rsspolymlp/common/property.py +75 -0
  23. rsspolymlp-0.0.2/src/rsspolymlp/rss/__init__.py +1 -0
  24. rsspolymlp-0.0.2/src/rsspolymlp/rss/load_logfile.py +138 -0
  25. rsspolymlp-0.0.2/src/rsspolymlp/rss/random_struct.py +221 -0
  26. rsspolymlp-0.0.2/src/rsspolymlp/rss/rss_analysis.py +363 -0
  27. rsspolymlp-0.0.2/src/rsspolymlp/rss/rss_mlp.py +246 -0
  28. rsspolymlp-0.0.2/src/rsspolymlp/rss/rss_parallel.py +130 -0
  29. rsspolymlp-0.0.2/src/rsspolymlp/utils/__init__.py +1 -0
  30. rsspolymlp-0.0.2/src/rsspolymlp/utils/ground_state_e.py +35 -0
  31. rsspolymlp-0.0.2/src/rsspolymlp/utils/lammps_utils.py +240 -0
  32. rsspolymlp-0.0.2/src/rsspolymlp/utils/matplot_util/__init__.py +1 -0
  33. rsspolymlp-0.0.2/src/rsspolymlp/utils/matplot_util/custom_plt.py +103 -0
  34. rsspolymlp-0.0.2/src/rsspolymlp/utils/matplot_util/examples/__init__.py +1 -0
  35. rsspolymlp-0.0.2/src/rsspolymlp/utils/matplot_util/examples/example.py +122 -0
  36. rsspolymlp-0.0.2/src/rsspolymlp/utils/matplot_util/examples/template.py +72 -0
  37. rsspolymlp-0.0.2/src/rsspolymlp/utils/matplot_util/make_plot.py +338 -0
  38. rsspolymlp-0.0.2/src/rsspolymlp/utils/optimum.py +30 -0
  39. rsspolymlp-0.0.2/src/rsspolymlp/utils/pymatgen_utils.py +143 -0
  40. rsspolymlp-0.0.2/src/rsspolymlp/utils/spglib_utils.py +232 -0
  41. rsspolymlp-0.0.2/src/rsspolymlp/utils/wait_readfile.py +32 -0
  42. rsspolymlp-0.0.2/src/rsspolymlp.egg-info/PKG-INFO +95 -0
  43. rsspolymlp-0.0.2/src/rsspolymlp.egg-info/SOURCES.txt +45 -0
  44. rsspolymlp-0.0.2/src/rsspolymlp.egg-info/dependency_links.txt +1 -0
  45. rsspolymlp-0.0.2/src/rsspolymlp.egg-info/entry_points.txt +8 -0
  46. rsspolymlp-0.0.2/src/rsspolymlp.egg-info/requires.txt +20 -0
  47. rsspolymlp-0.0.2/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,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: rsspolymlp
3
+ Version: 0.0.2
4
+ Summary: A framework for random structure search using polynomial MLPs
5
+ Author-email: Hayato Wakai <wakai@cms.mtl.kyoto-u.ac.jp>
6
+ License: MIT
7
+ Project-URL: homepage, https://github.com/hytwakai/rsspolymlp
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: numpy
12
+ Requires-Dist: scipy
13
+ Requires-Dist: pypolymlp
14
+ Requires-Dist: symfc
15
+ Requires-Dist: spglib
16
+ Requires-Dist: joblib
17
+ Provides-Extra: pymatgen
18
+ Requires-Dist: pymatgen; extra == "pymatgen"
19
+ Provides-Extra: matplotlib
20
+ Requires-Dist: matplotlib; extra == "matplotlib"
21
+ Provides-Extra: seaborn
22
+ Requires-Dist: seaborn; extra == "seaborn"
23
+ Provides-Extra: tools
24
+ Requires-Dist: pymatgen; extra == "tools"
25
+ Requires-Dist: matplotlib; extra == "tools"
26
+ Requires-Dist: seaborn; extra == "tools"
27
+ Dynamic: license-file
28
+
29
+ # A framework for random structure search (RSS) using polynomial MLPs
30
+
31
+ ## Citation of rsspolymlp
32
+
33
+ If you use `rsspolymlp` in your study, please cite the following articles.
34
+
35
+ “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/)
36
+ ```
37
+ @article{HayatoWakai202323053,
38
+ title="{Efficient global crystal structure prediction using polynomial machine learning potential in the binary Al–Cu alloy system}",
39
+ author={Hayato Wakai and Atsuto Seko and Isao Tanaka},
40
+ journal={J. Ceram. Soc. Jpn.},
41
+ volume={131},
42
+ number={10},
43
+ pages={762-766},
44
+ year={2023},
45
+ doi={10.2109/jcersj2.23053}
46
+ }
47
+ ```
48
+
49
+ ## Installation
50
+
51
+ ### Required libraries and python modules
52
+
53
+ - python >= 3.9
54
+ - pypolymlp
55
+ - symfc
56
+ - spglib
57
+ - joblib
58
+
59
+ [Optional]
60
+ - matplotlib (if plotting RSS results)
61
+ - seaborn (if plotting RSS results)
62
+
63
+ ### How to install
64
+
65
+ ```shell
66
+ git clone https://github.com/hytwakai/rsspolymlp.git
67
+ cd rsspolymlp
68
+ conda create -n rsspolymlp
69
+ conda activate rsspolymlp
70
+ conda install -c conda-forge pypolymlp symfc spglib joblib
71
+ pip install .
72
+ ```
73
+
74
+ ## Usage
75
+
76
+ The command-line interface of `rsspolylmp` is organized into three sections, each corresponding to a different phase of the workflow:
77
+ 1. Generating initial structures (`rss-init-struct`)
78
+ 2. Performing parallel geometry optimization (`rss-parallel`)
79
+ 3. Analyzing RSS results (`rss-analysis`)
80
+
81
+ ### Example Commands
82
+
83
+ ```shell
84
+ rss-init-struct --elements Al Cu --atom_counts 4 4 --num_init_str 2000
85
+ rss-parallel --pot polymlp.yaml --num_opt_str 1000
86
+ rss-analysis
87
+ ```
88
+
89
+ #### Arguments
90
+ - `--elements`: List of element symbols (e.g., `Al Cu`).
91
+ - `--atom_counts`: Number of atoms for each element (must match the order of `--elements`).
92
+ - `--num_init_str`: Number of random initial structures to generate. *(default: 5000)*
93
+ - `--pot`: Path to the polynomial MLP potential file. *(default: polymlp.yaml)*
94
+ - `--num_opt_str`: Maximum number of optimized structures to obtain from RSS. *(default: 1000)*
95
+ - [Additional information is here](docs/rss.md)
@@ -0,0 +1,67 @@
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.9
26
+ - pypolymlp
27
+ - symfc
28
+ - spglib
29
+ - joblib
30
+
31
+ [Optional]
32
+ - matplotlib (if plotting RSS results)
33
+ - seaborn (if plotting RSS results)
34
+
35
+ ### How to install
36
+
37
+ ```shell
38
+ git clone https://github.com/hytwakai/rsspolymlp.git
39
+ cd rsspolymlp
40
+ conda create -n rsspolymlp
41
+ conda activate rsspolymlp
42
+ conda install -c conda-forge pypolymlp symfc spglib joblib
43
+ pip install .
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ The command-line interface of `rsspolylmp` is organized into three sections, each corresponding to a different phase of the workflow:
49
+ 1. Generating initial structures (`rss-init-struct`)
50
+ 2. Performing parallel geometry optimization (`rss-parallel`)
51
+ 3. Analyzing RSS results (`rss-analysis`)
52
+
53
+ ### Example Commands
54
+
55
+ ```shell
56
+ rss-init-struct --elements Al Cu --atom_counts 4 4 --num_init_str 2000
57
+ rss-parallel --pot polymlp.yaml --num_opt_str 1000
58
+ rss-analysis
59
+ ```
60
+
61
+ #### Arguments
62
+ - `--elements`: List of element symbols (e.g., `Al Cu`).
63
+ - `--atom_counts`: Number of atoms for each element (must match the order of `--elements`).
64
+ - `--num_init_str`: Number of random initial structures to generate. *(default: 5000)*
65
+ - `--pot`: Path to the polynomial MLP potential file. *(default: polymlp.yaml)*
66
+ - `--num_opt_str`: Maximum number of optimized structures to obtain from RSS. *(default: 1000)*
67
+ - [Additional information is here](docs/rss.md)
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rsspolymlp"
7
+ version = "0.0.2"
8
+ description = "A framework for random structure search using polynomial MLPs"
9
+ license = {text = "MIT"}
10
+ authors = [
11
+ {name = "Hayato Wakai", email = "wakai@cms.mtl.kyoto-u.ac.jp"},
12
+ ]
13
+ readme = {file = "README.md", content-type = "text/markdown"}
14
+ requires-python = ">=3.9"
15
+ dependencies = [
16
+ "numpy",
17
+ "scipy",
18
+ "pypolymlp",
19
+ "symfc",
20
+ "spglib",
21
+ "joblib",
22
+ ]
23
+
24
+ [project.optional-dependencies]
25
+ pymatgen = ["pymatgen"]
26
+ matplotlib = ["matplotlib"]
27
+ seaborn = ["seaborn"]
28
+ tools = ["pymatgen", "matplotlib", "seaborn"]
29
+
30
+ [project.urls]
31
+ homepage = "https://github.com/hytwakai/rsspolymlp"
32
+
33
+ [project.scripts]
34
+ rss-init-struct = "rsspolymlp.rss.random_struct:run"
35
+ rss-single-srun = "rsspolymlp.rss.rss_parallel:run_single_srun"
36
+ rss-parallel = "rsspolymlp.rss.rss_parallel:run"
37
+ rss-analysis = "rsspolymlp.rss.rss_analysis:run"
38
+ rss-summarize = "rsspolymlp.analysis.rss_summarize:run"
39
+ rss-outlier = "rsspolymlp.analysis.outlier_cands:run"
40
+ plot-binary = "rsspolymlp.analysis.plot.binary:run"
41
+
42
+ [tool.setuptools]
43
+ package-dir = {"" = "src"}
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["src"]
47
+
48
+ [tool.setuptools.package-data]
49
+ 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,117 @@
1
+ import numpy as np
2
+ from scipy.spatial import ConvexHull
3
+
4
+ from rsspolymlp.analysis.rss_summarize import (
5
+ extract_composition_ratio,
6
+ load_rss_results,
7
+ )
8
+
9
+
10
+ class ConvexHullAnalyzer:
11
+
12
+ def __init__(self, elements, result_paths):
13
+
14
+ self.elements = elements
15
+ self.result_paths = result_paths
16
+ self.rss_result_fe = {}
17
+ self.ch_obj = None
18
+ self.fe_ch = None
19
+ self.comp_ch = None
20
+ self.poscar_ch = None
21
+
22
+ def run_calc(self):
23
+ self.calc_formation_e()
24
+ self.calc_convex_hull()
25
+ self.calc_fe_above_convex_hull()
26
+
27
+ def calc_formation_e(self):
28
+ for res_path in self.result_paths:
29
+ comp_res = extract_composition_ratio(res_path, self.elements)
30
+ comp_ratio = tuple(
31
+ np.round(np.array(comp_res.comp_ratio) / sum(comp_res.comp_ratio), 10)
32
+ )
33
+
34
+ rss_results = load_rss_results(
35
+ res_path, absolute_path=True, get_warning=True
36
+ )
37
+ rss_results_valid = [r for r in rss_results if not r["is_strong_outlier"]]
38
+ rss_results_array = {
39
+ "formation_e": np.array([r["enthalpy"] for r in rss_results_valid]),
40
+ "poscars": np.array([r["poscar"] for r in rss_results_valid]),
41
+ "is_outliers": np.array(
42
+ [r["is_weak_outlier"] for r in rss_results_valid]
43
+ ),
44
+ }
45
+ self.rss_result_fe[comp_ratio] = rss_results_array
46
+
47
+ e_ends = []
48
+ keys = np.array(list(self.rss_result_fe))
49
+ valid_keys = keys[np.any(keys == 1, axis=1)]
50
+ sorted_keys = sorted(valid_keys, key=lambda x: np.argmax(x))
51
+ for key in sorted_keys:
52
+ key_tuple = tuple(key)
53
+ is_outlier = self.rss_result_fe[key_tuple]["is_outliers"]
54
+ first_valid_index = np.where(~is_outlier)[0][0]
55
+ energy = self.rss_result_fe[key_tuple]["formation_e"][first_valid_index]
56
+ e_ends.append(energy)
57
+ e_ends = np.array(e_ends)
58
+
59
+ for key in self.rss_result_fe:
60
+ self.rss_result_fe[key]["formation_e"] -= np.dot(e_ends, np.array(key))
61
+
62
+ def calc_convex_hull(self):
63
+ rss_result_fe = self.rss_result_fe
64
+
65
+ comp_list, e_min_list, label_list = [], [], []
66
+ for key, dicts in rss_result_fe.items():
67
+ comp_list.append(key)
68
+ first_idx = np.where(~dicts["is_outliers"])[0][0]
69
+ e_min_list.append(dicts["formation_e"][first_idx])
70
+ label_list.append(dicts["poscars"][first_idx])
71
+
72
+ comp_array = np.array(comp_list)
73
+ e_min_array = np.array(e_min_list).reshape(-1, 1)
74
+ label_array = np.array(label_list)
75
+
76
+ data_ch = np.hstack([comp_array[:, 1:], e_min_array])
77
+ self.ch_obj = ConvexHull(data_ch)
78
+
79
+ v_convex = np.unique(self.ch_obj.simplices)
80
+ _fe_ch = e_min_array[v_convex].astype(float)
81
+ mask = np.where(_fe_ch <= 1e-10)[0]
82
+
83
+ _comp_ch = comp_array[v_convex][mask]
84
+ sort_idx = np.lexsort(_comp_ch[:, ::-1].T)
85
+
86
+ self.fe_ch = _fe_ch[mask][sort_idx]
87
+ self.comp_ch = _comp_ch[sort_idx]
88
+ self.poscar_ch = label_array[v_convex][mask][sort_idx]
89
+
90
+ def calc_fe_above_convex_hull(self):
91
+ rss_result_fe = self.rss_result_fe
92
+ for key in rss_result_fe:
93
+ _ehull = self._calc_fe_convex_hull(key)
94
+ fe_above_ch = rss_result_fe[key]["formation_e"] - _ehull
95
+ rss_result_fe[key]["fe_above_ch"] = fe_above_ch
96
+
97
+ def _calc_fe_convex_hull(self, comp_ratio):
98
+ ehull = -1e10
99
+ for eq in self.ch_obj.equations:
100
+ face_val_comp = -(np.dot(eq[:-2], comp_ratio[1:]) + eq[-1])
101
+ ehull_trial = face_val_comp / eq[-2]
102
+ if ehull_trial > ehull and abs(ehull_trial) > 1e-8:
103
+ ehull = ehull_trial
104
+
105
+ return ehull
106
+
107
+ def get_struct_near_convex_hull(self, threshold):
108
+ near_ch = {}
109
+ rss_result_fe = self.rss_result_fe
110
+ for key in rss_result_fe:
111
+ is_near = rss_result_fe[key]["fe_above_ch"] < threshold / 1000
112
+ near_ch[key]["formation_e"] = rss_result_fe[key]["formation_e"][is_near]
113
+ near_ch[key]["poscars"] = rss_result_fe[key]["poscars"][is_near]
114
+ near_ch[key]["is_outliers"] = rss_result_fe[key]["is_outliers"][is_near]
115
+ near_ch[key]["fe_above_ch"] = rss_result_fe[key]["fe_above_ch"][is_near]
116
+
117
+ return near_ch
@@ -0,0 +1,76 @@
1
+ import argparse
2
+ import os
3
+ import shutil
4
+
5
+ import numpy as np
6
+
7
+
8
+ def detect_outlier(energies: np.array):
9
+ """
10
+ Detect outliers and potential outliers in a 1D energy array.
11
+
12
+ Returns
13
+ -------
14
+ is_strong_outlier: np.ndarray of bool
15
+ Boolean array marking strong outliers (energy diff > 1.0).
16
+ is_weak_outlier : np.ndarray of bool
17
+ Boolean array marking potential outliers (energy diff > 0.2).
18
+ """
19
+ is_strong_outlier = np.full(energies.shape, False, dtype=bool)
20
+ is_weak_outlier = np.full(energies.shape, False, dtype=bool)
21
+ window = 5
22
+
23
+ n = len(energies)
24
+ if n < 2:
25
+ return is_strong_outlier, is_weak_outlier
26
+
27
+ for i in range(n - 1):
28
+ end = min(i + 1 + window, n)
29
+ energy_diff = np.abs(energies[i] - energies[i + 1 : end])
30
+ if np.any(energy_diff > 1.0):
31
+ is_strong_outlier[i] = True
32
+ if np.any(energy_diff > 0.1):
33
+ is_weak_outlier[i] = True
34
+ else:
35
+ break
36
+
37
+ return is_strong_outlier, is_weak_outlier
38
+
39
+
40
+ def run():
41
+ from rsspolymlp.analysis.rss_summarize import load_rss_results
42
+
43
+ parser = argparse.ArgumentParser()
44
+ parser.add_argument(
45
+ "--result_paths",
46
+ nargs="*",
47
+ type=str,
48
+ required=True,
49
+ help="Path(s) to RSS result log file(s).",
50
+ )
51
+ args = parser.parse_args()
52
+
53
+ # Prepare output directory: remove existing files if already exists
54
+ out_dir = "outlier_candidates"
55
+ if os.path.exists(out_dir):
56
+ for filename in os.listdir(out_dir):
57
+ if "POSCAR" in filename:
58
+ file_path = os.path.join(out_dir, filename)
59
+ if os.path.isfile(file_path):
60
+ os.remove(file_path)
61
+ else:
62
+ os.makedirs(out_dir)
63
+
64
+ # Copy weak outlier POSCARs
65
+ for res_path in args.result_paths:
66
+ logname = os.path.basename(res_path).split(".log")[0]
67
+ rss_results = load_rss_results(res_path, absolute_path=True, get_warning=True)
68
+
69
+ for idx, result in enumerate(rss_results):
70
+ if result.get("is_weak_outlier"):
71
+ dest = f"outlier_candidates/POSCAR_{logname}_No{idx + 1}"
72
+ shutil.copy(result["poscar"], dest)
73
+
74
+
75
+ if __name__ == "__main__":
76
+ run()
@@ -0,0 +1,114 @@
1
+ import argparse
2
+
3
+ import numpy as np
4
+
5
+ from rsspolymlp.analysis.convex_hull import ConvexHullAnalyzer
6
+ from rsspolymlp.utils.matplot_util.custom_plt import CustomPlt
7
+ from rsspolymlp.utils.matplot_util.make_plot import MakePlot
8
+
9
+
10
+ def run():
11
+ parser = argparse.ArgumentParser()
12
+ parser.add_argument(
13
+ "--elements",
14
+ nargs=2,
15
+ type=str,
16
+ required=True,
17
+ help="Two chemical elements, e.g., La Bi",
18
+ )
19
+ parser.add_argument(
20
+ "--result_paths",
21
+ nargs="+",
22
+ type=str,
23
+ required=True,
24
+ help="Paths to RSS result log files",
25
+ )
26
+ parser.add_argument(
27
+ "--threshold",
28
+ type=float,
29
+ default=-1,
30
+ help="Threshold for energy above the convex hull in meV/atom "
31
+ "(default: -1 means no threshold applied)",
32
+ )
33
+ args = parser.parse_args()
34
+
35
+ custom_template = CustomPlt(
36
+ label_size=8,
37
+ label_pad=3.0,
38
+ legend_size=7,
39
+ xtick_size=7,
40
+ ytick_size=7,
41
+ xtick_pad=3.0,
42
+ ytick_pad=3.0,
43
+ )
44
+ plt = custom_template.get_custom_plt()
45
+ plotter = MakePlot(
46
+ plt=plt,
47
+ column_size=1,
48
+ height_ratio=0.8,
49
+ )
50
+ plotter.initialize_ax()
51
+
52
+ ch_analyzer = ConvexHullAnalyzer(args.elements, args.result_paths)
53
+ ch_analyzer.run_calc()
54
+
55
+ fe_ch = ch_analyzer.fe_ch
56
+ comp_ch = ch_analyzer.comp_ch
57
+ rss_result_fe = ch_analyzer.rss_result_fe
58
+
59
+ plotter.set_visuality(n_color=4, n_line=4, n_marker=1, color_type="grad")
60
+ plotter.ax_plot(
61
+ comp_ch[:, 1],
62
+ fe_ch,
63
+ plot_type="closed",
64
+ label=None,
65
+ plot_size=0.7,
66
+ line_size=1,
67
+ zorder=2,
68
+ )
69
+ fe_min = np.min(fe_ch)
70
+
71
+ for key, _dict in rss_result_fe.items():
72
+ plotter.set_visuality(n_color=3, n_line=0, n_marker=0, color_type="grad")
73
+ if not args.threshold == -1:
74
+ is_not_near = (
75
+ _dict["fe_above_ch"][~_dict["is_outliers"]] > args.threshold / 1000
76
+ )
77
+ _energies = _dict["formation_e"][~_dict["is_outliers"]][is_not_near]
78
+ _comps = np.full_like(_energies, fill_value=key[1])
79
+ plotter.ax_scatter(
80
+ _comps, _energies, plot_type="open", label=None, plot_size=0.4
81
+ )
82
+
83
+ plotter.set_visuality(n_color=1, n_line=0, n_marker=1)
84
+ _energies = _dict["formation_e"][~_dict["is_outliers"]][~is_not_near]
85
+ _comps = np.full_like(_energies, fill_value=key[1])
86
+ plotter.ax_scatter(
87
+ _comps, _energies, plot_type="open", label=None, plot_size=0.5
88
+ )
89
+ else:
90
+ _energies = _dict["formation_e"][~_dict["is_outliers"]]
91
+ _comps = np.full_like(_energies, fill_value=key[1])
92
+ plotter.ax_scatter(
93
+ _comps, _energies, plot_type="open", label=None, plot_size=0.4
94
+ )
95
+
96
+ plotter.finalize_ax(
97
+ xlabel=rf"$x$ in {args.elements[0]}$_{{1-x}}${args.elements[1]}$_{{x}}$",
98
+ ylabel="Formation energy (eV/atom)",
99
+ x_limits=[0, 1],
100
+ x_grid=[0.2, 0.1],
101
+ y_limits=[fe_min * 1.1, 0],
102
+ )
103
+
104
+ plt.tight_layout()
105
+ plt.savefig(
106
+ f"{args.elements[0]}{args.elements[1]}.png",
107
+ bbox_inches="tight",
108
+ pad_inches=0.01,
109
+ dpi=600,
110
+ )
111
+
112
+
113
+ if __name__ == "__main__":
114
+ run()