packmol-util 0.2.4__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.
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: packmol_util
3
+ Version: 0.2.4
4
+ Summary: using packmol
5
+ Author-email: Kengo NAKADA <kengo.nakada@email.com>
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: xlogger-py
9
+ Requires-Dist: dataclasses
10
+ Requires-Dist: coloredlogs
11
+ Requires-Dist: colorlog
12
+ Requires-Dist: pytest
13
+ Requires-Dist: pytest-asyncio
14
+ Requires-Dist: pyyaml
15
+ Requires-Dist: toml
16
+ Requires-Dist: nest_asyncio
17
+ Requires-Dist: sphinx
18
+ Requires-Dist: myst-parser
19
+ Requires-Dist: sphinx-autobuild
20
+ Requires-Dist: sphinx-rtd-theme
21
+ Requires-Dist: sphinxcontrib-napoleon
22
+ Requires-Dist: linkify-it-py
23
+
24
+ # packmol_util
25
+ [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/shimane-dev/rmc_dft/blob/main/README.md)
26
+ [![ja](https://img.shields.io/badge/lang-ja-yellow.svg)](https://github.com/shimane-dev/rmc_dft/blob/main/README.ja.md)
27
+
28
+
29
+ A **Python utility kit for automatically generating amorphous initial structures (POSCAR)** for first-principles calculations (VASP) and molecular dynamics (MD) simulations using Packmol.
30
+
31
+ By specifying only the stoichiometric ratio, density (mass density or number density), and total number of atoms, this tool automatically completes the full workflow: generating the Packmol input file, running Packmol, and outputting a POSCAR in VASP format with Direct coordinates.
32
+
33
+ ---
34
+
35
+ ## ๐Ÿ’ก Features
36
+
37
+ 1. **Intuitive density specification**
38
+ - Packmol itself does not interpret density or atomic weights, but this tool performs the required internal calculations and determines the cubic cell length $L$ that satisfies the specified density.
39
+ 2. **Exclusion radius specification by element symbol**
40
+ - This hides Packmol's inconvenient atom-index-based specification inside XYZ files. Interatomic avoidance distances can be controlled using an intuitive element-name-based dictionary, such as `{"P": 0.25, "O": 1.05}`.
41
+ 3. **Preservation of molecular units (geometries)**
42
+ - For systems such as $\text{Li}_3\text{PO}_4$, amorphization through mixing and packing can be performed while preserving intramolecular bond lengths, such as those in $\text{PO}_4$ tetrahedra.
43
+ 4. **Extremely simple high-level API**
44
+ - By using `AmorphousBuilder`, the cumbersome file relay workflow (`.inp` construction $\rightarrow$ external execution $\rightarrow$ coordinate conversion) can be condensed into only a few lines of script.
45
+
46
+ ---
47
+
48
+ ## ๐Ÿ“ Directory structure
49
+
50
+ ```text
51
+ src/
52
+ โ”œโ”€โ”€ packmol_util/
53
+ โ”‚
54
+ โ”‚ โ”œโ”€โ”€ __init__.py # Package initialization file
55
+ โ”‚ โ”œโ”€โ”€ builder.py # High-level integrated builder (AmorphousBuilder) โ˜…NEW
56
+ โ”‚ โ”œโ”€โ”€ model.py # Template XYZ generation and structure-definition module
57
+ โ”‚ โ”œโ”€โ”€ packmol.py # Core density calculation, Packmol execution, and POSCAR conversion
58
+ โ”‚ โ”œโ”€โ”€ packmol_inp.py # Parsing engine for constructing packmol.inp
59
+ โ”‚ โ”œโ”€โ”€ param.py # Atomic weights and physical constants for the periodic table
60
+
61
+ โ””โ”€โ”€ example/ # Location for material-specific generation scripts
62
+ โ”œโ”€โ”€ Li3PO4/
63
+ โ”‚ โ””โ”€โ”€ make_Li3PO4.py
64
+ โ””โ”€โ”€ FeSiB/
65
+ โ””โ”€โ”€ make_FeSiB.py
66
+ ```
@@ -0,0 +1,43 @@
1
+ # packmol_util
2
+ [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/shimane-dev/rmc_dft/blob/main/README.md)
3
+ [![ja](https://img.shields.io/badge/lang-ja-yellow.svg)](https://github.com/shimane-dev/rmc_dft/blob/main/README.ja.md)
4
+
5
+
6
+ A **Python utility kit for automatically generating amorphous initial structures (POSCAR)** for first-principles calculations (VASP) and molecular dynamics (MD) simulations using Packmol.
7
+
8
+ By specifying only the stoichiometric ratio, density (mass density or number density), and total number of atoms, this tool automatically completes the full workflow: generating the Packmol input file, running Packmol, and outputting a POSCAR in VASP format with Direct coordinates.
9
+
10
+ ---
11
+
12
+ ## ๐Ÿ’ก Features
13
+
14
+ 1. **Intuitive density specification**
15
+ - Packmol itself does not interpret density or atomic weights, but this tool performs the required internal calculations and determines the cubic cell length $L$ that satisfies the specified density.
16
+ 2. **Exclusion radius specification by element symbol**
17
+ - This hides Packmol's inconvenient atom-index-based specification inside XYZ files. Interatomic avoidance distances can be controlled using an intuitive element-name-based dictionary, such as `{"P": 0.25, "O": 1.05}`.
18
+ 3. **Preservation of molecular units (geometries)**
19
+ - For systems such as $\text{Li}_3\text{PO}_4$, amorphization through mixing and packing can be performed while preserving intramolecular bond lengths, such as those in $\text{PO}_4$ tetrahedra.
20
+ 4. **Extremely simple high-level API**
21
+ - By using `AmorphousBuilder`, the cumbersome file relay workflow (`.inp` construction $\rightarrow$ external execution $\rightarrow$ coordinate conversion) can be condensed into only a few lines of script.
22
+
23
+ ---
24
+
25
+ ## ๐Ÿ“ Directory structure
26
+
27
+ ```text
28
+ src/
29
+ โ”œโ”€โ”€ packmol_util/
30
+ โ”‚
31
+ โ”‚ โ”œโ”€โ”€ __init__.py # Package initialization file
32
+ โ”‚ โ”œโ”€โ”€ builder.py # High-level integrated builder (AmorphousBuilder) โ˜…NEW
33
+ โ”‚ โ”œโ”€โ”€ model.py # Template XYZ generation and structure-definition module
34
+ โ”‚ โ”œโ”€โ”€ packmol.py # Core density calculation, Packmol execution, and POSCAR conversion
35
+ โ”‚ โ”œโ”€โ”€ packmol_inp.py # Parsing engine for constructing packmol.inp
36
+ โ”‚ โ”œโ”€โ”€ param.py # Atomic weights and physical constants for the periodic table
37
+
38
+ โ””โ”€โ”€ example/ # Location for material-specific generation scripts
39
+ โ”œโ”€โ”€ Li3PO4/
40
+ โ”‚ โ””โ”€โ”€ make_Li3PO4.py
41
+ โ””โ”€โ”€ FeSiB/
42
+ โ””โ”€โ”€ make_FeSiB.py
43
+ ```
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["setuptools>=71.0","coloredlogs"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "packmol_util"
7
+ version = "0.2.4"
8
+
9
+ description = "using packmol"
10
+ authors = [
11
+ { name="Kengo NAKADA", email="kengo.nakada@email.com" },
12
+ ]
13
+ readme = "README.md"
14
+ # requires-python = ">=3.8"
15
+ requires-python = ">=3.10"
16
+ dependencies = [
17
+ #"x_logger",
18
+ "xlogger-py",
19
+ "dataclasses",
20
+ "coloredlogs",
21
+ "colorlog",
22
+ "pytest",
23
+ "pytest-asyncio",
24
+ "pyyaml",
25
+ "toml",
26
+ # "grpcio",
27
+ "nest_asyncio",
28
+ "sphinx",
29
+ "myst-parser",
30
+ "sphinx-autobuild",
31
+ "sphinx-rtd-theme",
32
+ "sphinxcontrib-napoleon",
33
+ "linkify-it-py"
34
+ ]
35
+
36
+ [tool.pytest.ini_options]
37
+ pythonpath = ["src"]
38
+
39
+ # ใ“ใ“ใงpytestใฎiniใจๅŒใ˜ใ‚ˆใ†ใซใ‚ชใƒ—ใ‚ทใƒงใƒณใŒๆ›ธใ‘ใ‚‹
40
+ # ไพ‹ใˆใฐใƒ†ใ‚นใƒˆใƒžใƒผใ‚ฏใ€ใƒ•ใ‚ฃใƒซใ‚ฟใ€ใ‚ซใƒใƒฌใƒƒใ‚ธใชใฉใ‚‚่ฟฝ่จ˜ๅฏ่ƒฝ
41
+
42
+ [tool.black]
43
+ line-length = 88
44
+
45
+ [tool.isort]
46
+ profile = "black"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,3 @@
1
+ from packmol_util.builder import AmorphousBuilder
2
+ from packmol_util.config import Config
3
+ from packmol_util.packmol import Packmol
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ packmol_util/builder.py
4
+
5
+ ่ฉณ็ดฐใชใƒ•ใ‚กใ‚คใƒซใฎใƒใ‚ฑใƒ„ใƒชใƒฌใƒผใ‚’้š ่”ฝใ—ใ€
6
+ ๆ•ฐ่กŒใงใ‚ขใƒขใƒซใƒ•ใ‚กใ‚นๆง‹้€ ใ‚’ไฝœๆˆใ™ใ‚‹ใŸใ‚ใฎ้ซ˜ใƒฌใƒ™ใƒซ็ตฑๅˆใƒขใ‚ธใƒฅใƒผใƒซใ€‚
7
+
8
+ ไปฅไธ‹ใฎ 3 ็‚นใ‚’ๅค–้ƒจๅ…ฅๅŠ›ใฎๅŸบๆบ–ใจใ™ใ‚‹๏ผš
9
+ 1. ๅŒ–ๅญฆ้‡่ซ–ๆฏ”๏ผˆๆ•ดๆ•ฐๆฏ”๏ผ‰
10
+ 2. ๅฏ†ๅบฆ [g/cm^3] ใพใŸใฏ ๆ•ฐๅฏ†ๅบฆ [1/ร…^3] ใฎใ„ใšใ‚Œใ‹ไธ€ๆ–น
11
+ 3. ใ‚ปใƒซๅ†…ใฎ็ทๅŽŸๅญๆ•ฐ total_atom_count
12
+
13
+ ใ€้‡่ฆใ€‘ๆŒ‡ๅฎšใ—ใŸ total_atom_count ใŒ ratios ใฎๅˆ่จˆใงๅ‰ฒใ‚Šๅˆ‡ใ‚Œใชใ„๏ผˆ็ต„ๆˆๆฏ”ใ‚’็ถญๆŒใงใใชใ„๏ผ‰ๅ ดๅˆใ€
14
+ ใƒ—ใƒญใ‚ฐใƒฉใƒ ใŒ็ ด็ถปใ™ใ‚‹ใฎใ‚’้˜ฒใใŸใ‚ใ€ๆŒ‡ๅฎšใ•ใ‚ŒใŸๅŽŸๅญๆ•ฐใซๆœ€ใ‚‚่ฟ‘ใ„ใ€Œๅ‰ฒใ‚Šๅˆ‡ใ‚Œใ‚‹็ทๅŽŸๅญๆ•ฐใ€ใธ่‡ชๅ‹•่ฃœๆญฃใ‚’่กŒใ†ใ€‚
15
+ ๅ†…้ƒจใง่จˆ็ฎ—ใ•ใ‚ŒใŸๅ…จไฝ“ใฎใ‚นใ‚ฑใƒผใƒซๅ€็އ๏ผˆmultiplier๏ผ‰ใ‚’ๅค–้ƒจใซๅ…ฌ้–‹๏ผˆใƒ—ใƒญใƒ‘ใƒ†ใ‚ฃๅŒ–๏ผ‰ใ™ใ‚‹ใ“ใจใงใ€
16
+ ๅˆฉ็”จๅดใฎใ‚นใ‚ฏใƒชใƒ—ใƒˆใŒใƒฆใƒ‹ใƒƒใƒˆๆง‹ๆˆๅ˜ไฝใงใฎใƒ‘ใƒƒใ‚ญใƒณใ‚ฐๅ€‹ๆ•ฐใ‚’ไธ€ๅ…ƒ็ฎก็†ใงใใ‚‹ใ‚ˆใ†ใซ่จญ่จˆใ•ใ‚Œใฆใ„ใ‚‹ใ€‚
17
+ """
18
+
19
+ from typing import Dict, List, Optional, Tuple
20
+ from packmol_util.packmol import Packmol
21
+ from packmol_util.packmol_inp import PackmolInp
22
+
23
+
24
+ class AmorphousBuilder:
25
+
26
+ def __init__(
27
+ self,
28
+ symbols: List[str],
29
+ ratios: List[int],
30
+ total_atom_count: int, # ๅธŒๆœ›ใ™ใ‚‹ใƒ™ใƒผใ‚นใฎ็ทๅŽŸๅญๆ•ฐ
31
+ minimum_separation: float,
32
+ mass_density: Optional[float] = None,
33
+ number_density: Optional[float] = None,
34
+ packmol_bin: str = "packmol",
35
+ ):
36
+ if len(symbols) != len(ratios):
37
+ raise ValueError("symbols ใจ ratios ใฎ้•ทใ•ใŒไธ€่‡ดใ—ใฆใ„ใพใ›ใ‚“ใ€‚")
38
+
39
+ if total_atom_count <= 0:
40
+ raise ValueError("total_atom_count ใฏๆญฃใฎๆ•ดๆ•ฐใงใ‚ใ‚‹ๅฟ…่ฆใŒใ‚ใ‚Šใพใ™ใ€‚")
41
+
42
+ self.symbols = symbols
43
+ self.ratios = ratios
44
+ self.minimum_separation = minimum_separation
45
+ self.packmol_bin = packmol_bin
46
+
47
+ # --- ใ€่‡ชๅ‹•่ฃœๆญฃใƒญใ‚ธใƒƒใ‚ฏใ€‘ๆŒ‡ๅฎšใงใใชใ„ๆฏ”็އ๏ผˆๅ‰ฒใ‚Šๅˆ‡ใ‚Œใชใ„ๆ•ฐ๏ผ‰ใฎใจใใฎ่‡ชๅ‹•่ฃœๆญฃ ---
48
+ ratio_sum = sum(ratios)
49
+ if total_atom_count % ratio_sum != 0:
50
+ # ๆœ€ใ‚‚่ฟ‘ใ„ๆ•ดๆ•ฐๅ€๏ผˆๅ€็އ๏ผ‰ใ‚’่จˆ็ฎ—
51
+ multiplier = round(total_atom_count / ratio_sum)
52
+ if multiplier < 1:
53
+ multiplier = 1
54
+ adjusted_atom_count = ratio_sum * multiplier
55
+ print(
56
+ f"[Warning] ๆŒ‡ๅฎšใ•ใ‚ŒใŸ็ทๅŽŸๅญๆ•ฐ {total_atom_count} ใฏ็ต„ๆˆๆฏ”ใฎๅˆ่จˆ {ratio_sum} ใงๅ‰ฒใ‚Šๅˆ‡ใ‚Œใพใ›ใ‚“ใ€‚"
57
+ )
58
+ print(
59
+ f" ็ต„ๆˆๆฏ”ใ‚’็ถญๆŒใ™ใ‚‹ใŸใ‚ใ€็ทๅŽŸๅญๆ•ฐใ‚’ {adjusted_atom_count} (ๅ€็އ: {multiplier}) ใซ่‡ชๅ‹•่ชฟๆ•ดใ—ใพใ—ใŸใ€‚"
60
+ )
61
+ self.total_atom_count = adjusted_atom_count
62
+ self._multiplier = multiplier
63
+ else:
64
+ self.total_atom_count = total_atom_count
65
+ self._multiplier = total_atom_count // ratio_sum
66
+
67
+ # ๅฏ†ๅบฆใฎ่จˆ็ฎ—ใจใ‚ปใƒซไธ€่พบใฎ้•ทใ•ๆฑบๅฎš
68
+ self.packmol_runner = Packmol()
69
+ if number_density is not None:
70
+ self.box_length = self.packmol_runner.cell_length_from_number_density(
71
+ self.total_atom_count, number_density
72
+ )
73
+ elif mass_density is not None:
74
+ n_density = self.packmol_runner.number_density_from_mass_density(
75
+ symbols, ratios, mass_density
76
+ )
77
+ self.box_length = self.packmol_runner.cell_length_from_number_density(
78
+ self.total_atom_count, n_density
79
+ )
80
+ else:
81
+ raise ValueError(
82
+ "mass_density ใพใŸใฏ number_density ใฎใฉใกใ‚‰ใ‹ใ‚’ๆŒ‡ๅฎšใ—ใฆใใ ใ•ใ„ใ€‚"
83
+ )
84
+
85
+ self.templates: List[dict] = []
86
+
87
+ @property
88
+ def multiplier(self) -> int:
89
+ """
90
+ ๅ…จไฝ“ใฎๅŒ–ๅญฆ้‡่ซ–ๆฏ”ใซๅฏพใ™ใ‚‹ใ‚ปใƒซใฎใ‚นใ‚ฑใƒผใƒซๅ€็އใ‚’่ฟ”ใ™ใ€‚
91
+ ๅˆฉ็”จๅดใฎใ‚นใ‚ฏใƒชใƒ—ใƒˆใฏใ€ใ“ใฎๅ€็އใ‚’ใƒฆใƒ‹ใƒƒใƒˆใฎ1ๅผใ‚ใŸใ‚Šๆง‹ๆˆๆฏ”ใซไน—็ฎ—ใ™ใ‚‹ใ“ใจใงใ€
92
+ Packmol ใซๅผ•ใๆธกใ™ในใๅฎŸ้š›ใฎ่ค‡่ฃฝๅ€‹ๆ•ฐ๏ผˆnumber๏ผ‰ใ‚’็ฎ—ๅ‡บใ™ใ‚‹ใ€‚
93
+ """
94
+ return self._multiplier
95
+
96
+ def add_template(
97
+ self,
98
+ xyz_file: str,
99
+ number: int, # ใใฎใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’ Packmol ใŒใ‚ปใƒซๅ†…ใซไฝ•ๅ€‹้…็ฝฎใ™ใ‚‹ใ‹ใ‚’่กจใ™ๅฎŸ้š›ใฎ่ค‡่ฃฝๅ€‹ๆ•ฐ
100
+ packing_radii: Optional[Dict[str, float]] = None,
101
+ fnc_pairs_in_template: Optional[List[Tuple[int, int, int]]] = None,
102
+ fnc_distance_ranges: Optional[Dict[int, Tuple[float, float]]] = None,
103
+ # packmolๅพŒใซใ€FNCไฝœๆˆใงใฎๅฝข็Šถๅ›บๅฎšใฎ้ธๆŠž
104
+ fnc_output_constraint_types: Optional[List[int]] = None,
105
+ ):
106
+ """
107
+ Packmol ใฎ 1 ใคใฎ structure ใƒ–ใƒญใƒƒใ‚ฏใซๅฏพๅฟœใ™ใ‚‹ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๆƒ…ๅ ฑใ‚’็™ป้Œฒใ™ใ‚‹ใ€‚
108
+
109
+ ๅผ•ๆ•ฐใฎ่ชฌๆ˜Ž๏ผš
110
+ xyz_file: Packmol ใซๆธกใ™ๅ˜ๅŽŸๅญใพใŸใฏๅˆ†ๅญใƒฆใƒ‹ใƒƒใƒˆใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใฎๅบงๆจ™ใƒ•ใ‚กใ‚คใƒซๅ (.xyz)
111
+ number: ใใฎใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’ไฝ•ๅ€‹่ค‡่ฃฝใ—ใฆ้…็ฝฎใ™ใ‚‹ใ‹ใ‚’ๆŒ‡ๅฎšใ™ใ‚‹ [int]
112
+ packing_radii: Packmol ้…็ฝฎๆ™‚ใซไฝฟใ†ๅ…ƒ็ด ่จ˜ๅทๅˆฅใฎๆŽ’้™คๅŠๅพ„ใฎ่พžๆ›ธ๏ผˆไปปๆ„๏ผ‰ใ€‚
113
+ ๅˆ†ๅญใƒฆใƒ‹ใƒƒใƒˆๅ†…้ƒจใฎ็ตๅˆ่ท้›ขใ‚„ๅฝข็Šถใ‚’ไฝœใ‚‹ๅ€คใงใฏใชใ„ใ€‚
114
+ fnc_pairs_in_template: ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๅ†…ใฎๅฑ€ๆ‰€ๅŽŸๅญ็•ชๅทใงๆŒ‡ๅฎšใ—ใŸ FNC ใƒšใ‚ขใ€‚
115
+ (local_i, local_j, constraint_type) ใฎใƒชใ‚นใƒˆใงๆŒ‡ๅฎšใ™ใ‚‹ใ€‚
116
+ fnc_distance_ranges: FNC constraint type ใ”ใจใฎ่ท้›ข็ฏ„ๅ›ฒใ€‚
117
+ {type: (rmin, rmax)} ใงๆŒ‡ๅฎšใ™ใ‚‹ใ€‚
118
+ fnc_output_constraint_types: .fnc ใซๅฎŸ้š›ใซๅ‡บๅŠ›ใ™ใ‚‹ constraint type ใฎใƒชใ‚นใƒˆใ€‚
119
+ None ใฎๅ ดๅˆใฏๅ…จ type ใ‚’ๅ‡บๅŠ›ใ™ใ‚‹ใ€‚
120
+ ไพ‹: [1] ใ‚’ๆŒ‡ๅฎšใ™ใ‚‹ใจ type 1 ใฎใƒšใ‚ขใ ใ‘ใ‚’ๅ‡บๅŠ›ใ™ใ‚‹ใ€‚
121
+ Packmol ็”จ XYZ ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใฎๅฝข็Šถใ‚„้…็ฝฎใซใฏๅฝฑ้Ÿฟใ—ใชใ„ใ€‚
122
+ """
123
+ # builder ใงใฏใ€ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’ใพใ  Packmol ๅ…ฅๅŠ›ใธๅค‰ๆ›ใ—ใชใ„ใ€‚
124
+ # ใ“ใ“ใงใฏใ€Œใฉใฎ XYZ ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‚’ไฝ•ๅ€‹็ฝฎใใ‹ใ€ใจใ€ๅฟ…่ฆใชใ‚‰้…็ฝฎ็”จ packing ๅŠๅพ„ใ ใ‘ใ‚’ไฟๅญ˜ใ™ใ‚‹ใ€‚
125
+ # ๅ…ทไฝ“็š„ใช structure ... end structure ใธใฎๅค‰ๆ›ใฏ build() ๅ†…ใฎ PackmolInp ใŒๆ‹…ๅฝ“ใ™ใ‚‹ใ€‚
126
+ self.templates.append(
127
+ {
128
+ "xyz": xyz_file,
129
+ "number": number,
130
+ "radii": packing_radii,
131
+ "fnc_pairs_in_template": fnc_pairs_in_template,
132
+ "fnc_distance_ranges": fnc_distance_ranges,
133
+ # Packmol ใฏ xyz_file ใฎใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๅฝข็Šถใ‚’ใใฎใพใพ้…็ฝฎใ™ใ‚‹ใ€‚
134
+ # ใ“ใฎๆŒ‡ๅฎšใฏ Packmol ใซใฏๆธกใ•ใšใ€Packmol ๅพŒใซ .fnc ใ‚’ไฝœใ‚‹ๆฎต้šŽใ ใ‘ใงไฝฟใ†ใ€‚
135
+ # None: ๅ…จ FNC type ใ‚’ๅ‡บๅŠ›ใ™ใ‚‹ใ€‚
136
+ # [1]: type 1 ใฎๆ‹˜ๆŸใ ใ‘ใ‚’ .fnc ใซๅ‡บๅŠ›ใ™ใ‚‹ใ€‚
137
+ "fnc_output_constraint_types": fnc_output_constraint_types,
138
+ }
139
+ )
140
+
141
+ def build(self, output_prefix: str, fnc_file: Optional[str] = None):
142
+ # output_prefix ใฏใ€ไธ€้€ฃใฎ็”Ÿๆˆใƒ•ใ‚กใ‚คใƒซๅใฎๆŽฅ้ ญ่พžใจใ—ใฆไฝฟใ†ใ€‚
143
+ # ไพ‹: output_prefix="Li3PO4" ใฎๅ ดๅˆใ€Li3PO4_packmol.inp / Li3PO4_packmol.xyz / Li3PO4_packmol.log ใ‚’ไฝœใ‚‹ใ€‚
144
+ input_file = f"{output_prefix}_packmol.inp"
145
+ output_xyz = f"{output_prefix}_packmol.xyz"
146
+ output_log = f"{output_prefix}_packmol.log"
147
+ poscar_file = "POSCAR"
148
+ vasp_file = "POSCAR.vasp"
149
+ if fnc_file is None:
150
+ fnc_file = f"{output_prefix}.fnc"
151
+
152
+ # 1. PackmolInp ใ‚’ๆง‹็ฏ‰ใ™ใ‚‹ใ€‚
153
+ # AmorphousBuilder ใŒๆŒใคๆƒ…ๅ ฑใฏ Python ๅดใฎๆŠฝ่ฑก่กจ็พใงใ‚ใ‚‹ใ€‚
154
+ # PackmolInp ใฏใ€ใใ‚Œใ‚’ Packmol ใฎ .inp ๆ–‡ๆณ•ใธ่ฝใจใ™ๅฝนๅ‰ฒใ‚’ๆŒใคใ€‚
155
+ inp_builder = PackmolInp(
156
+ minimum_separation_distance=self.minimum_separation,
157
+ output_xyz=output_xyz,
158
+ box_length=self.box_length,
159
+ use_pbc=True,
160
+ )
161
+ for t in self.templates:
162
+ # 1 ไปถใฎ template ใฏ Packmol ๅ…ฅๅŠ›ใฎ 1 ใคใฎ structure ใƒ–ใƒญใƒƒใ‚ฏใซๅฏพๅฟœใ™ใ‚‹ใ€‚
163
+ # ๅ˜ๅŽŸๅญใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใงใ‚‚ PO4 ใฎใ‚ˆใ†ใชๅˆ†ๅญใƒฆใƒ‹ใƒƒใƒˆใงใ‚‚ใ€ใ“ใ“ใงใฏๅŒใ˜็™ป้Œฒๅ‡ฆ็†ใ‚’้€šใ™ใ€‚
164
+ inp_builder.add_structure(
165
+ xyz_file=t["xyz"],
166
+ number=t["number"],
167
+ packing_radii_by_atom_symbol=t["radii"],
168
+ )
169
+ # ็™ป้Œฒใ—ใŸ template ็พคใ‚’ Packmol ๅ…ฅๅŠ›ใƒ•ใ‚กใ‚คใƒซใจใ—ใฆๆ›ธใๅ‡บใ™ใ€‚
170
+ inp_builder.write(input_file)
171
+
172
+ # 2. Packmol ใ‚’ๅฎŸ่กŒใ—ใ€็”Ÿๆˆใ•ใ‚ŒใŸ XYZ ใ‚’ POSCAR ใซๅค‰ๆ›ใ™ใ‚‹ใ€‚
173
+ self.packmol_runner.run_packmol(
174
+ input_file=input_file,
175
+ output_log=output_log,
176
+ packmol_bin=self.packmol_bin,
177
+ )
178
+
179
+ # Packmol ๅ‡บๅŠ›ใฏ Cartesian ๅบงๆจ™ใชใฎใงใ€VASP POSCAR ็”จใซ Direct ๅบงๆจ™ใธๅค‰ๆ›ใ™ใ‚‹ใ€‚
180
+ xyz_to_poscar_index = self.packmol_runner.xyz_to_poscar(
181
+ xyz_file=output_xyz,
182
+ poscar_file=poscar_file,
183
+ packmol_inp=input_file,
184
+ comment=f"{output_prefix} amorphous initial model generated by Packmol (Total Atoms: {self.total_atom_count})",
185
+ elements=self.symbols,
186
+ )
187
+
188
+ self._write_fnc_from_templates(
189
+ output_xyz=output_xyz,
190
+ xyz_to_poscar_index=xyz_to_poscar_index,
191
+ fnc_file=fnc_file,
192
+ output_prefix=output_prefix,
193
+ )
194
+
195
+ # ๅค–้ƒจใƒ„ใƒผใƒซ้€ฃๆบ็”จใซ vasp.xyz๏ผˆใ‚ใ‚‹ใ„ใฏ POSCAR.vasp๏ผ‰ใ‚‚ๅŒๆง˜ใซๅ‡บๅŠ›ใ™ใ‚‹ใ€‚
196
+ self.packmol_runner.xyz_to_poscar(
197
+ xyz_file=output_xyz,
198
+ poscar_file=vasp_file,
199
+ packmol_inp=input_file,
200
+ comment=f"{output_prefix} amorphous initial model vasp format",
201
+ elements=self.symbols,
202
+ )
203
+
204
+ def _write_fnc_from_templates(
205
+ self,
206
+ output_xyz: str,
207
+ xyz_to_poscar_index: Dict[int, int],
208
+ fnc_file: str,
209
+ output_prefix: str,
210
+ ) -> None:
211
+ """
212
+ ็™ป้Œฒๆธˆใฟใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใฎ FNC ๅฑ€ๆ‰€ใƒšใ‚ขใ‚’ใ€ๆœ€็ต‚ POSCAR/cfg ็•ชๅทใธๅค‰ๆ›ใ—ใฆ *.fnc ใ‚’ๆ›ธใใ€‚
213
+
214
+ Packmol ๅ‡บๅŠ› XYZ ใงใฏใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๅ˜ไฝใฎๅŽŸๅญ้ †ใŒๆฎ‹ใ‚‹ใ€‚ไธ€ๆ–นใ€POSCAR ๅค‰ๆ›ใงใฏ
215
+ ๅ…ƒ็ด ใ”ใจใซๅบงๆจ™ใ‚’ไธฆในๆ›ฟใˆใ‚‹ใŸใ‚ใ€PO4 ใฎ P ใจ O ใฎ็•ชๅทใฏ้›ขใ‚Œใ‚‹ใ€‚
216
+ ใ“ใฎใƒกใ‚ฝใƒƒใƒ‰ใฏใ€Packmol XYZ ็•ชๅทใ‚’ไธญ้–“็•ชๅทใจใ—ใฆไฝฟใ„ใ€
217
+ ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๅ†…ๅฑ€ๆ‰€็•ชๅทใ‹ใ‚‰ๆœ€็ต‚ POSCAR/cfg ็•ชๅทใฎ FNC ใƒšใ‚ขใ‚’ไฝœใ‚‹ใ€‚
218
+ """
219
+ all_fnc_pairs: List[Tuple[int, int, int]] = []
220
+ merged_distance_ranges: Dict[int, Tuple[float, float]] = {}
221
+
222
+ xyz_start_index = 1
223
+ for template in self.templates:
224
+ xyz_file = str(template["xyz"])
225
+ number = int(template["number"])
226
+ fnc_pairs_in_template = template.get("fnc_pairs_in_template")
227
+ fnc_distance_ranges = template.get("fnc_distance_ranges")
228
+ # packmolๅพŒใซๅฝข็Šถๅ›บๅฎšใงใฎFNCใฎ้ธๆŠžๆœ‰็„ก
229
+ fnc_output_constraint_types = template.get("fnc_output_constraint_types")
230
+
231
+ atom_count_in_template = self.packmol_runner.count_atoms_in_xyz(
232
+ xyz_file=xyz_file,
233
+ )
234
+
235
+ if fnc_pairs_in_template is not None:
236
+ if fnc_distance_ranges is None:
237
+ raise ValueError(
238
+ f"{xyz_file} ใซ fnc_pairs_in_template ใŒๆŒ‡ๅฎšใ•ใ‚Œใฆใ„ใ‚‹ใŒใ€"
239
+ "fnc_distance_ranges ใŒๆŒ‡ๅฎšใ•ใ‚Œใฆใ„ใชใ„ใ€‚"
240
+ )
241
+ ########################################
242
+ # Packmol ็”จใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๆง‹้€ ใฏๅค‰ๆ›ดใ—ใชใ„ใ€‚
243
+ # ใ“ใ“ใงใฏ Packmol ๅพŒใฎๆœ€็ต‚ๆง‹้€ ใ‹ใ‚‰ .fnc ใ‚’ไฝœใ‚‹ๆฎต้šŽใ ใ‘ใงใ€
244
+ # RMC ใซๆธกใ™ constraint type ใ‚’้ธๅˆฅใ™ใ‚‹ใ€‚
245
+ if fnc_output_constraint_types is None:
246
+ active_constraint_types = list(fnc_distance_ranges.keys())
247
+ else:
248
+ active_constraint_types = list(fnc_output_constraint_types)
249
+
250
+ active_constraint_type_set = set(active_constraint_types)
251
+
252
+ for type_index in active_constraint_types:
253
+ if type_index not in fnc_distance_ranges:
254
+ raise ValueError(
255
+ f"{xyz_file} ใฎ fnc_output_constraint_types ใซ type={type_index} "
256
+ "ใŒๆŒ‡ๅฎšใ•ใ‚Œใฆใ„ใ‚‹ใŒใ€fnc_distance_ranges ใซๅญ˜ๅœจใ—ใชใ„ใ€‚"
257
+ )
258
+
259
+ active_fnc_pairs_in_template = [
260
+ pair
261
+ for pair in fnc_pairs_in_template
262
+ if pair[2] in active_constraint_type_set
263
+ ]
264
+
265
+ active_fnc_distance_ranges = {
266
+ type_index: fnc_distance_ranges[type_index]
267
+ for type_index in active_constraint_types
268
+ }
269
+ ############################
270
+
271
+ for type_index, distance_range in active_fnc_distance_ranges.items():
272
+ if type_index in merged_distance_ranges:
273
+ if merged_distance_ranges[type_index] != distance_range:
274
+ raise ValueError(
275
+ "ๅŒใ˜ FNC constraint type ใซ็•ฐใชใ‚‹่ท้›ข็ฏ„ๅ›ฒใŒๆŒ‡ๅฎšใ•ใ‚Œใฆใ„ใ‚‹ใ€‚"
276
+ f" type={type_index}"
277
+ )
278
+ else:
279
+ merged_distance_ranges[type_index] = distance_range
280
+
281
+ copy_index = 0
282
+ while copy_index < number:
283
+ molecule_xyz_start = (
284
+ xyz_start_index + copy_index * atom_count_in_template
285
+ )
286
+ for (
287
+ local_i,
288
+ local_j,
289
+ constraint_type,
290
+ ) in active_fnc_pairs_in_template:
291
+ xyz_i = molecule_xyz_start + local_i - 1
292
+ xyz_j = molecule_xyz_start + local_j - 1
293
+ poscar_i = xyz_to_poscar_index[xyz_i]
294
+ poscar_j = xyz_to_poscar_index[xyz_j]
295
+ all_fnc_pairs.append((poscar_i, poscar_j, constraint_type))
296
+ copy_index = copy_index + 1
297
+
298
+ xyz_start_index = xyz_start_index + number * atom_count_in_template
299
+
300
+ if len(all_fnc_pairs) == 0:
301
+ return
302
+
303
+ self.packmol_runner.write_fnc_file(
304
+ fnc_file=fnc_file,
305
+ total_atom_count=self.total_atom_count,
306
+ fnc_pairs=all_fnc_pairs,
307
+ distance_ranges_by_type=merged_distance_ranges,
308
+ title=f"{output_prefix} FNC generated from Packmol template mapping",
309
+ )
310
+
311
+
312
+ #
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """packmol_util ใฎ่จญๅฎšไฟๆŒใ‚ฏใƒฉใ‚นใ€‚"""
4
+
5
+ from typing import Optional, Sequence, Union
6
+ import os
7
+
8
+ import yaml
9
+ from x_logger import XLogger
10
+
11
+
12
+ class Config:
13
+ """packmol_util ็”จใฎ่จญๅฎšๅ€คใ‚’ไฟๆŒใ™ใ‚‹ใ€‚"""
14
+
15
+ N_tot: int = 2000
16
+ mass_density_g_cm3: float = 7.3336
17
+ number_density_A3: float = 0.09119
18
+ element_info: Union[str, Sequence[str]] = ["Fe", "Si", "B"]
19
+ ratios: Sequence[int] = [82, 4, 14]
20
+
21
+ packmol_bin: str = "packmol"
22
+ minimum_separation: float = 2.0
23
+ output_prefix: str = "amorphous"
24
+
25
+ def __init__(
26
+ self,
27
+ yaml_path: Optional[str] = None,
28
+ logger: Optional[XLogger] = None,
29
+ ) -> None:
30
+ if logger is None:
31
+ logger = XLogger(logger_name=__name__)
32
+ self._logger = logger
33
+
34
+ if yaml_path:
35
+ self._load_yaml(yaml_path)
36
+
37
+ @classmethod
38
+ def load_yaml(cls, yaml_path: Optional[str] = None) -> None:
39
+ """YAML ใฎๅ†…ๅฎนใงใ‚ฏใƒฉใ‚นๅค‰ๆ•ฐใ‚’ไธŠๆ›ธใใ™ใ‚‹ใ€‚"""
40
+ if yaml_path is None:
41
+ yaml_path = os.environ.get("PACKMOL_UTIL_CONFIG_YAML")
42
+ if yaml_path is None:
43
+ return
44
+
45
+ with open(yaml_path, "r") as f:
46
+ data = yaml.safe_load(f)
47
+
48
+ if data is None:
49
+ return
50
+
51
+ for key, value in data.items():
52
+ if hasattr(cls, key):
53
+ setattr(cls, key, value)
54
+ else:
55
+ print("NO SUCH VAR:", key)
56
+
57
+ def _load_yaml(self, yaml_path: Optional[str] = None) -> None:
58
+ """YAML ใฎๅ†…ๅฎนใงใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นๅฑžๆ€งใ‚’ไธŠๆ›ธใใ™ใ‚‹ใ€‚"""
59
+ self._logger.info(f"load_yaml: {yaml_path}")
60
+
61
+ if yaml_path is None:
62
+ yaml_path = os.environ.get("PACKMOL_UTIL_CONFIG_YAML")
63
+ if yaml_path is None:
64
+ return
65
+
66
+ yaml_path = os.path.abspath(yaml_path)
67
+ self._logger.info(f"abspath: {yaml_path}")
68
+
69
+ with open(yaml_path, "r") as f:
70
+ data = yaml.safe_load(f)
71
+
72
+ if data is None:
73
+ return
74
+
75
+ for key, value in data.items():
76
+ if hasattr(self, key):
77
+ setattr(self, key, value)
78
+ else:
79
+ print("NO SUCH VAR:", key)