pack-mm 0.0.15__tar.gz → 0.0.20__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.1
2
+ Name: pack-mm
3
+ Version: 0.0.20
4
+ Summary: packing materials and molecules in boxes using for machine learnt interatomic potentials
5
+ Author: Alin M. Elena
6
+ Classifier: Programming Language :: Python
7
+ Classifier: Programming Language :: Python :: 3.10
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Natural Language :: English
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Project-URL: Repository, https://github.com/ddmms/pack-mm/
15
+ Project-URL: Documentation, https://ddmms.github.io/pack-mm/
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: janus-core>=0.7.2
18
+ Requires-Dist: typer<1.0.0,>=0.12.5
19
+ Requires-Dist: typer-config<2.0.0,>=1.4.2
20
+ Description-Content-Type: text/markdown
21
+
22
+ [![Python versions][python-badge]][python-link]
23
+ [![Build Status][ci-badge]][ci-link]
24
+ [![Coverage Status][cov-badge]][cov-link]
25
+ [![License][license-badge]][license-link]
26
+
27
+ # what is packmm
28
+
29
+ packmm is a simple python package that allows to build atomistic and molecular
30
+ systems which are of interest for materials and molecular modelling.
31
+
32
+ It tries to generate realistic starting configuration by employing machine learnt
33
+ interatomic potential for describing interactions between atoms and Monte Carlo,
34
+ Molecular Dynamics and hybrid Monte Carlo.
35
+
36
+ It provides both a cli and a python api, with some examples below.
37
+
38
+ ## Quick install
39
+
40
+ ```bash
41
+
42
+ uv pip install pack-mm
43
+
44
+ ```
45
+ or install the lates
46
+
47
+ ```bash
48
+
49
+ uv pip install git+https://github.com/ddmms/pack-mm.git
50
+
51
+ ```
52
+
53
+ ## CLI examples
54
+
55
+
56
+ ### MOF in spherical pocket
57
+
58
+ ```bash
59
+
60
+ packmm --system examples/data/UiO-66.cif --molecule H2O --nmols 10 --where sphere --centre 10.0,10.0,10.0 --radius 5.0 --geometry
61
+
62
+ ```
63
+
64
+ ![](examples/pics/UiO66water.png)
65
+
66
+ ### Zeolite in cylindrical channel
67
+
68
+
69
+ ```bash
70
+
71
+ packmm --system examples/data/MFI.cif --molecule H2O --nmols 30 --where cylinderY --centre 10.0,10.0,13.0 --radius 3.5 --height 19.00 --no-geometry
72
+
73
+ ```
74
+
75
+ ![](examples/pics/MFIwater.png)
76
+
77
+ ### NaCl on surface
78
+
79
+ ```bash
80
+ packmm --system examples/data/NaCl.cif --molecule H2O --nmols 30 --where box --centre 8.5,8.5,16.0 --a 16.9 --b 16.9 --c 7.5 --no-geometry
81
+
82
+ ```
83
+
84
+ ![](examples/pics/NaClwater.png)
85
+
86
+ ### MOF ellipsoid
87
+
88
+ first add a methanol
89
+
90
+ ```bash
91
+
92
+ packmm --system examples/data/Cu2L.cif --molecule examples/data/Ethanol.xyz --nmols 1 --where sphere --centre 5.18,8.15,25.25 --radius 1 --model small-0b2 --geometry
93
+
94
+ ```
95
+
96
+ !()[examples/pics/Cu2L-ethanol.png]
97
+
98
+ ``` bash
99
+
100
+ packmm --system Cu2L-ethanol.cif --molecule H2O --nmols 10 --where ellipsoid --centre 5.18,8.15,25.25 --a 5.18 --b 8.15 --c 8.25 --no-geometry --model small-0b2
101
+
102
+
103
+ ```
104
+
105
+ !()[examples/pics/Cu2l-ethanol-water.png]
106
+
107
+ ### Liquid water
108
+
109
+ ```bash
110
+
111
+ packmm --molecule H2O --nmols 33 --where anywhere --cell-a 10.0 --cell-b 10.0 --cell-c 10.0 --model small-0b2
112
+
113
+
114
+ ```
115
+
116
+ !()[examples/pics/water.png]
117
+ ### interstitials
118
+
119
+ ```bash
120
+
121
+ packmm --system Pd-super.cif --molecule H2 --nmols 50 --where anywhere --model small-0b2
122
+
123
+ ```
124
+
125
+ before optimisation
126
+
127
+ !()[examples/pics/Pd-H2-noopt.png]
128
+
129
+
130
+ after optimisation
131
+
132
+ !()[examples/pics/Pd-H2.png]
133
+
134
+
135
+
136
+ ### full list of options
137
+
138
+ ```bash
139
+
140
+ packmm --help
141
+
142
+ Usage: packmm [OPTIONS]
143
+
144
+ Pack molecules into a system based on the specified parameters.
145
+
146
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮
147
+ │ --system TEXT The original box in which │
148
+ │ you want to add particles. │
149
+ │ If not provided, an empty │
150
+ │ box will be created. │
151
+ │ [default: None] │
152
+ │ --molecule TEXT Name of the molecule to be │
153
+ │ processed, ASE-recognizable │
154
+ │ or ASE-readable file. │
155
+ │ [default: H2O] │
156
+ │ --nmols INTEGER Target number of molecules │
157
+ │ to insert. │
158
+ │ [default: -1] │
159
+ │ --ntries INTEGER Maximum number of attempts │
160
+ │ to insert each molecule. │
161
+ │ [default: 50] │
162
+ │ --seed INTEGER Random seed for │
163
+ │ reproducibility. │
164
+ │ [default: 2025] │
165
+ │ --where [anywhere|sphere|box|cylin Where to insert the │
166
+ │ derZ|cylinderY|cylinderX|e molecule. Choices: │
167
+ │ llipsoid] 'anywhere', 'sphere', │
168
+ │ 'box', 'cylinderZ', │
169
+ │ 'cylinderY', 'cylinderX', │
170
+ │ 'ellipsoid'. │
171
+ │ [default: anywhere] │
172
+ │ --centre TEXT Centre of the insertion │
173
+ │ zone, coordinates in Å, │
174
+ │ e.g., '5.0, 5.0, 5.0'. │
175
+ │ [default: None] │
176
+ │ --radius FLOAT Radius of the sphere or │
177
+ │ cylinder in Å, depending on │
178
+ │ the insertion volume. │
179
+ │ [default: None] │
180
+ │ --height FLOAT Height of the cylinder in │
181
+ │ Å. │
182
+ │ [default: None] │
183
+ │ --a FLOAT Side of the box or │
184
+ │ semi-axis of the ellipsoid, │
185
+ │ in Å, depends on the │
186
+ │ insertion method. │
187
+ │ [default: None] │
188
+ │ --b FLOAT Side of the box or │
189
+ │ semi-axis of the ellipsoid, │
190
+ │ in Å, depends on the │
191
+ │ insertion method. │
192
+ │ [default: None] │
193
+ │ --c FLOAT Side of the box or │
194
+ │ semi-axis of the ellipsoid, │
195
+ │ in Å, depends on the │
196
+ │ insertion method. │
197
+ │ [default: None] │
198
+ │ --device TEXT Device to run calculations │
199
+ │ on (e.g., 'cpu' or 'cuda'). │
200
+ │ [default: cpu] │
201
+ │ --model TEXT ML model to use. │
202
+ │ [default: medium-omat-0] │
203
+ │ --arch TEXT MLIP architecture to use. │
204
+ │ [default: mace_mp] │
205
+ │ --temperature FLOAT Temperature for the Monte │
206
+ │ Carlo acceptance rule. │
207
+ │ [default: 300.0] │
208
+ │ --cell-a FLOAT Side of the empty box along │
209
+ │ the x-axis in Å. │
210
+ │ [default: 20.0] │
211
+ │ --cell-b FLOAT Side of the empty box along │
212
+ │ the y-axis in Å. │
213
+ │ [default: 20.0] │
214
+ │ --cell-c FLOAT Side of the empty box along │
215
+ │ the z-axis in Å. │
216
+ │ [default: 20.0] │
217
+ │ --fmax FLOAT force tollerance for │
218
+ │ optimisation if needed. │
219
+ │ [default: 0.1] │
220
+ │ --geometry --no-geometry Perform geometry │
221
+ │ optimization at the end. │
222
+ │ [default: geometry] │
223
+ │ --out-path TEXT path to save various │
224
+ │ outputs. │
225
+ │ [default: .] │
226
+ │ --install-completion Install completion for the │
227
+ │ current shell. │
228
+ │ --show-completion Show completion for the │
229
+ │ current shell, to copy it │
230
+ │ or customize the │
231
+ │ installation. │
232
+ │ --help Show this message and exit. │
233
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
234
+
235
+
236
+ ```
237
+
238
+
239
+ [python-badge]: https://img.shields.io/pypi/pyversions/pack-mm.svg
240
+ [python-link]: https://pypi.org/project/pack-mm/
241
+ [ci-badge]: https://github.com/ddmms/pack-mm/actions/workflows/build.yml/badge.svg?branch=main
242
+ [ci-link]: https://github.com/ddmms/pack-mm/actions
243
+ [cov-badge]: https://coveralls.io/repos/github/ddmms/pack-mm/badge.svg?branch=main
244
+ [cov-link]: https://coveralls.io/github/ddmms/pack-mm?branch=main
245
+ [license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
246
+ [license-link]: https://opensource.org/license/MIT
@@ -0,0 +1,225 @@
1
+ [![Python versions][python-badge]][python-link]
2
+ [![Build Status][ci-badge]][ci-link]
3
+ [![Coverage Status][cov-badge]][cov-link]
4
+ [![License][license-badge]][license-link]
5
+
6
+ # what is packmm
7
+
8
+ packmm is a simple python package that allows to build atomistic and molecular
9
+ systems which are of interest for materials and molecular modelling.
10
+
11
+ It tries to generate realistic starting configuration by employing machine learnt
12
+ interatomic potential for describing interactions between atoms and Monte Carlo,
13
+ Molecular Dynamics and hybrid Monte Carlo.
14
+
15
+ It provides both a cli and a python api, with some examples below.
16
+
17
+ ## Quick install
18
+
19
+ ```bash
20
+
21
+ uv pip install pack-mm
22
+
23
+ ```
24
+ or install the lates
25
+
26
+ ```bash
27
+
28
+ uv pip install git+https://github.com/ddmms/pack-mm.git
29
+
30
+ ```
31
+
32
+ ## CLI examples
33
+
34
+
35
+ ### MOF in spherical pocket
36
+
37
+ ```bash
38
+
39
+ packmm --system examples/data/UiO-66.cif --molecule H2O --nmols 10 --where sphere --centre 10.0,10.0,10.0 --radius 5.0 --geometry
40
+
41
+ ```
42
+
43
+ ![](examples/pics/UiO66water.png)
44
+
45
+ ### Zeolite in cylindrical channel
46
+
47
+
48
+ ```bash
49
+
50
+ packmm --system examples/data/MFI.cif --molecule H2O --nmols 30 --where cylinderY --centre 10.0,10.0,13.0 --radius 3.5 --height 19.00 --no-geometry
51
+
52
+ ```
53
+
54
+ ![](examples/pics/MFIwater.png)
55
+
56
+ ### NaCl on surface
57
+
58
+ ```bash
59
+ packmm --system examples/data/NaCl.cif --molecule H2O --nmols 30 --where box --centre 8.5,8.5,16.0 --a 16.9 --b 16.9 --c 7.5 --no-geometry
60
+
61
+ ```
62
+
63
+ ![](examples/pics/NaClwater.png)
64
+
65
+ ### MOF ellipsoid
66
+
67
+ first add a methanol
68
+
69
+ ```bash
70
+
71
+ packmm --system examples/data/Cu2L.cif --molecule examples/data/Ethanol.xyz --nmols 1 --where sphere --centre 5.18,8.15,25.25 --radius 1 --model small-0b2 --geometry
72
+
73
+ ```
74
+
75
+ !()[examples/pics/Cu2L-ethanol.png]
76
+
77
+ ``` bash
78
+
79
+ packmm --system Cu2L-ethanol.cif --molecule H2O --nmols 10 --where ellipsoid --centre 5.18,8.15,25.25 --a 5.18 --b 8.15 --c 8.25 --no-geometry --model small-0b2
80
+
81
+
82
+ ```
83
+
84
+ !()[examples/pics/Cu2l-ethanol-water.png]
85
+
86
+ ### Liquid water
87
+
88
+ ```bash
89
+
90
+ packmm --molecule H2O --nmols 33 --where anywhere --cell-a 10.0 --cell-b 10.0 --cell-c 10.0 --model small-0b2
91
+
92
+
93
+ ```
94
+
95
+ !()[examples/pics/water.png]
96
+ ### interstitials
97
+
98
+ ```bash
99
+
100
+ packmm --system Pd-super.cif --molecule H2 --nmols 50 --where anywhere --model small-0b2
101
+
102
+ ```
103
+
104
+ before optimisation
105
+
106
+ !()[examples/pics/Pd-H2-noopt.png]
107
+
108
+
109
+ after optimisation
110
+
111
+ !()[examples/pics/Pd-H2.png]
112
+
113
+
114
+
115
+ ### full list of options
116
+
117
+ ```bash
118
+
119
+ packmm --help
120
+
121
+ Usage: packmm [OPTIONS]
122
+
123
+ Pack molecules into a system based on the specified parameters.
124
+
125
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮
126
+ │ --system TEXT The original box in which │
127
+ │ you want to add particles. │
128
+ │ If not provided, an empty │
129
+ │ box will be created. │
130
+ │ [default: None] │
131
+ │ --molecule TEXT Name of the molecule to be │
132
+ │ processed, ASE-recognizable │
133
+ │ or ASE-readable file. │
134
+ │ [default: H2O] │
135
+ │ --nmols INTEGER Target number of molecules │
136
+ │ to insert. │
137
+ │ [default: -1] │
138
+ │ --ntries INTEGER Maximum number of attempts │
139
+ │ to insert each molecule. │
140
+ │ [default: 50] │
141
+ │ --seed INTEGER Random seed for │
142
+ │ reproducibility. │
143
+ │ [default: 2025] │
144
+ │ --where [anywhere|sphere|box|cylin Where to insert the │
145
+ │ derZ|cylinderY|cylinderX|e molecule. Choices: │
146
+ │ llipsoid] 'anywhere', 'sphere', │
147
+ │ 'box', 'cylinderZ', │
148
+ │ 'cylinderY', 'cylinderX', │
149
+ │ 'ellipsoid'. │
150
+ │ [default: anywhere] │
151
+ │ --centre TEXT Centre of the insertion │
152
+ │ zone, coordinates in Å, │
153
+ │ e.g., '5.0, 5.0, 5.0'. │
154
+ │ [default: None] │
155
+ │ --radius FLOAT Radius of the sphere or │
156
+ │ cylinder in Å, depending on │
157
+ │ the insertion volume. │
158
+ │ [default: None] │
159
+ │ --height FLOAT Height of the cylinder in │
160
+ │ Å. │
161
+ │ [default: None] │
162
+ │ --a FLOAT Side of the box or │
163
+ │ semi-axis of the ellipsoid, │
164
+ │ in Å, depends on the │
165
+ │ insertion method. │
166
+ │ [default: None] │
167
+ │ --b FLOAT Side of the box or │
168
+ │ semi-axis of the ellipsoid, │
169
+ │ in Å, depends on the │
170
+ │ insertion method. │
171
+ │ [default: None] │
172
+ │ --c FLOAT Side of the box or │
173
+ │ semi-axis of the ellipsoid, │
174
+ │ in Å, depends on the │
175
+ │ insertion method. │
176
+ │ [default: None] │
177
+ │ --device TEXT Device to run calculations │
178
+ │ on (e.g., 'cpu' or 'cuda'). │
179
+ │ [default: cpu] │
180
+ │ --model TEXT ML model to use. │
181
+ │ [default: medium-omat-0] │
182
+ │ --arch TEXT MLIP architecture to use. │
183
+ │ [default: mace_mp] │
184
+ │ --temperature FLOAT Temperature for the Monte │
185
+ │ Carlo acceptance rule. │
186
+ │ [default: 300.0] │
187
+ │ --cell-a FLOAT Side of the empty box along │
188
+ │ the x-axis in Å. │
189
+ │ [default: 20.0] │
190
+ │ --cell-b FLOAT Side of the empty box along │
191
+ │ the y-axis in Å. │
192
+ │ [default: 20.0] │
193
+ │ --cell-c FLOAT Side of the empty box along │
194
+ │ the z-axis in Å. │
195
+ │ [default: 20.0] │
196
+ │ --fmax FLOAT force tollerance for │
197
+ │ optimisation if needed. │
198
+ │ [default: 0.1] │
199
+ │ --geometry --no-geometry Perform geometry │
200
+ │ optimization at the end. │
201
+ │ [default: geometry] │
202
+ │ --out-path TEXT path to save various │
203
+ │ outputs. │
204
+ │ [default: .] │
205
+ │ --install-completion Install completion for the │
206
+ │ current shell. │
207
+ │ --show-completion Show completion for the │
208
+ │ current shell, to copy it │
209
+ │ or customize the │
210
+ │ installation. │
211
+ │ --help Show this message and exit. │
212
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
213
+
214
+
215
+ ```
216
+
217
+
218
+ [python-badge]: https://img.shields.io/pypi/pyversions/pack-mm.svg
219
+ [python-link]: https://pypi.org/project/pack-mm/
220
+ [ci-badge]: https://github.com/ddmms/pack-mm/actions/workflows/build.yml/badge.svg?branch=main
221
+ [ci-link]: https://github.com/ddmms/pack-mm/actions
222
+ [cov-badge]: https://coveralls.io/repos/github/ddmms/pack-mm/badge.svg?branch=main
223
+ [cov-link]: https://coveralls.io/github/ddmms/pack-mm?branch=main
224
+ [license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
225
+ [license-link]: https://opensource.org/license/MIT
@@ -52,30 +52,28 @@ def packmm(
52
52
  ),
53
53
  centre: str | None = typer.Option(
54
54
  None,
55
- help="""Centre of the insertion zone in fractional coordinates,
56
- e.g., '0.12,0.4,0.5'.""",
55
+ help="""Centre of the insertion zone, coordinates in Å,
56
+ e.g., '5.0, 5.0, 5.0'.""",
57
57
  ),
58
58
  radius: float | None = typer.Option(
59
59
  None,
60
60
  help="""Radius of the sphere or cylinder in Å,
61
61
  depending on the insertion volume.""",
62
62
  ),
63
- height: float | None = typer.Option(
64
- None, help="Height of the cylinder in fractional coordinates."
65
- ),
63
+ height: float | None = typer.Option(None, help="Height of the cylinder in Å."),
66
64
  a: float | None = typer.Option(
67
65
  None,
68
- help="""Side of the box or semi-axis of the ellipsoid, fractional,
66
+ help="""Side of the box or semi-axis of the ellipsoid, in Å,
69
67
  depends on the insertion method.""",
70
68
  ),
71
69
  b: float | None = typer.Option(
72
70
  None,
73
- help="""Side of the box or semi-axis of the ellipsoid, fractional,
71
+ help="""Side of the box or semi-axis of the ellipsoid, in Å,
74
72
  depends on the insertion method.""",
75
73
  ),
76
74
  c: float | None = typer.Option(
77
75
  None,
78
- help="""Side of the box or semi-axis of the ellipsoid, fractional,
76
+ help="""Side of the box or semi-axis of the ellipsoid, in Å,
79
77
  depends on the insertion method.""",
80
78
  ),
81
79
  device: str = typer.Option(
@@ -86,15 +84,22 @@ def packmm(
86
84
  temperature: float = typer.Option(
87
85
  300.0, help="Temperature for the Monte Carlo acceptance rule."
88
86
  ),
89
- cell_a: float = typer.Option(20.0, help="Side of the empty box along the x-axis."),
90
- cell_b: float = typer.Option(20.0, help="Side of the empty box along the y-axis."),
91
- cell_c: float = typer.Option(20.0, help="Side of the empty box along the z-axis."),
87
+ cell_a: float = typer.Option(
88
+ 20.0, help="Side of the empty box along the x-axis in Å."
89
+ ),
90
+ cell_b: float = typer.Option(
91
+ 20.0, help="Side of the empty box along the y-axis in Å."
92
+ ),
93
+ cell_c: float = typer.Option(
94
+ 20.0, help="Side of the empty box along the z-axis in Å."
95
+ ),
92
96
  fmax: float = typer.Option(
93
97
  0.1, help="force tollerance for optimisation if needed."
94
98
  ),
95
99
  geometry: bool = typer.Option(
96
100
  True, help="Perform geometry optimization at the end."
97
101
  ),
102
+ out_path: str = typer.Option(".", help="path to save various outputs."),
98
103
  ):
99
104
  """Pack molecules into a system based on the specified parameters."""
100
105
  print("Script called with following input")
@@ -119,6 +124,7 @@ def packmm(
119
124
  print(f"{temperature=}")
120
125
  print(f"{fmax=}")
121
126
  print(f"{geometry=}")
127
+ print(f"{out_path=}")
122
128
  if nmols == -1:
123
129
  print("nothing to do, no molecule to insert")
124
130
  raise typer.Exit(0)
@@ -151,9 +157,10 @@ def packmm(
151
157
  ntries=ntries,
152
158
  fmax=fmax,
153
159
  geometry=geometry,
154
- ca=cell_a,
155
- cb=cell_b,
156
- cc=cell_c,
160
+ cell_a=cell_a,
161
+ cell_b=cell_b,
162
+ cell_c=cell_c,
163
+ out_path=out_path,
157
164
  )
158
165
 
159
166
 
@@ -11,6 +11,7 @@ from pathlib import Path
11
11
  from ase import Atoms
12
12
  from ase.build import molecule as build_molecule
13
13
  from ase.io import read, write
14
+ from ase.units import kB
14
15
  from janus_core.calculations.geom_opt import GeomOpt
15
16
  from janus_core.helpers.mlip_calculators import choose_calculator
16
17
  from numpy import cos, exp, pi, random, sin, sqrt
@@ -138,28 +139,29 @@ def validate_value(label, x):
138
139
 
139
140
 
140
141
  def pack_molecules(
141
- system: str | None,
142
- molecule: str,
143
- nmols: int,
144
- arch: str,
145
- model: str,
146
- device: str,
147
- where: str,
148
- center: tuple[float, float, float] | None,
149
- radius: float | None,
150
- height: float | None,
151
- a: float | None,
152
- b: float | None,
153
- c: float | None,
154
- seed: int,
155
- temperature: float,
156
- ntries: int,
157
- geometry: bool,
158
- fmax: float,
159
- ca: float,
160
- cb: float,
161
- cc: float,
162
- ) -> None:
142
+ system: str = None,
143
+ molecule: str = "H2O",
144
+ nmols: int = -1,
145
+ arch: str = "cpu",
146
+ model: str = "mace_mp",
147
+ device: str = "medium-omat-0",
148
+ where: str = "anywhere",
149
+ center: tuple[float, float, float] = None,
150
+ radius: float = None,
151
+ height: float = None,
152
+ a: float = None,
153
+ b: float = None,
154
+ c: float = None,
155
+ seed: int = 2025,
156
+ temperature: float = 300.0,
157
+ ntries: int = 50,
158
+ geometry: bool = False,
159
+ fmax: float = 0.1,
160
+ cell_a: float = None,
161
+ cell_b: float = None,
162
+ cell_c: float = None,
163
+ out_path: str = ".",
164
+ ) -> float:
163
165
  """
164
166
  Pack molecules into a system based on the specified parameters.
165
167
 
@@ -181,9 +183,10 @@ def pack_molecules(
181
183
  temperature (float): Temperature in Kelvin for acceptance probability.
182
184
  ntries (int): Maximum number of attempts to insert each molecule.
183
185
  geometry (bool): Whether to perform geometry optimization after insertion.
184
- ca, cb, cc (float): Cell dimensions if system is empty.
186
+ cell_a, cell_b, cell_c (float): Cell dimensions if system is empty.
187
+ out_path (str): path to save various outputs
185
188
  """
186
- kbt = temperature * 8.6173303e-5 # Boltzmann constant in eV/K
189
+ kbt = temperature * kB
187
190
  validate_value("temperature", temperature)
188
191
  validate_value("radius", radius)
189
192
  validate_value("height", height)
@@ -193,10 +196,10 @@ def pack_molecules(
193
196
  validate_value("box b", b)
194
197
  validate_value("box c", c)
195
198
  validate_value("ntries", ntries)
196
- validate_value("cell box a", ca)
197
- validate_value("cell box b", cb)
199
+ validate_value("cell box cell a", cell_a)
200
+ validate_value("cell box cell b", cell_b)
198
201
  validate_value("nmols", nmols)
199
- validate_value("cell box c", cc)
202
+ validate_value("cell box cell c", cell_c)
200
203
 
201
204
  random.seed(seed)
202
205
 
@@ -204,25 +207,21 @@ def pack_molecules(
204
207
  sys = read(system)
205
208
  sysname = Path(system).stem
206
209
  except Exception:
207
- sys = Atoms(cell=[ca, cb, cc], pbc=[True, True, True])
210
+ sys = Atoms(cell=[cell_a, cell_b, cell_c], pbc=[True, True, True])
208
211
  sysname = "empty"
209
212
 
210
213
  cell = sys.cell.lengths()
211
214
 
212
- # Print initial information
215
+ # Print summary
213
216
  print(f"Inserting {nmols} {molecule} molecules in {sysname}.")
214
217
  print(f"Using {arch} model {model} on {device}.")
215
218
  print(f"Insert in {where}.")
216
219
 
217
- # Set center of insertion region
218
220
  if center is None:
219
221
  center = (cell[0] * 0.5, cell[1] * 0.5, cell[2] * 0.5)
220
- else:
221
- center = tuple(ci * cell[i] for i, ci in enumerate(center))
222
222
 
223
- # Set parameters based on insertion region
224
223
  if where == "anywhere":
225
- a, b, c = 1, 1, 1
224
+ a, b, c = cell[0], cell[1], cell[2]
226
225
  elif where == "sphere":
227
226
  if radius is None:
228
227
  radius = min(cell) * 0.5
@@ -230,20 +229,22 @@ def pack_molecules(
230
229
  if radius is None:
231
230
  if where == "cylinderZ":
232
231
  radius = min(cell[0], cell[1]) * 0.5
232
+ if height is None:
233
+ height = 0.5 * cell[2]
233
234
  elif where == "cylinderY":
234
235
  radius = min(cell[0], cell[2]) * 0.5
236
+ if height is None:
237
+ height = 0.5 * cell[1]
235
238
  elif where == "cylinderX":
236
239
  radius = min(cell[2], cell[1]) * 0.5
237
- if height is None:
238
- height = 0.5
240
+ if height is None:
241
+ height = 0.5 * cell[0]
239
242
  elif where == "box":
240
- a, b, c = a or 1, b or 1, c or 1
243
+ a, b, c = a or cell[0], b or cell[1], c or cell[2]
241
244
  elif where == "ellipsoid":
242
- a, b, c = a or 0.5, b or 0.5, c or 0.5
245
+ a, b, c = a or cell[0], b or cell[1], c or cell[2]
243
246
 
244
- calc = choose_calculator(
245
- arch=arch, model_path=model, device=device, default_dtype="float64"
246
- )
247
+ calc = choose_calculator(arch=arch, model_path=model, device=device)
247
248
  sys.calc = calc
248
249
 
249
250
  e = sys.get_potential_energy() if len(sys) > 0 else 0.0
@@ -253,7 +254,7 @@ def pack_molecules(
253
254
  accept = False
254
255
  for _itry in range(ntries):
255
256
  mol = load_molecule(molecule)
256
- tv = get_insertion_position(where, center, cell, a, b, c, radius, height)
257
+ tv = get_insertion_position(where, center, a, b, c, radius, height)
257
258
  mol = rotate_molecule(mol)
258
259
  mol.translate(tv)
259
260
 
@@ -274,18 +275,31 @@ def pack_molecules(
274
275
  csys = tsys.copy()
275
276
  e = en
276
277
  print(f"Inserted particle {i + 1}")
277
- write(f"{sysname}+{i + 1}{Path(molecule).stem}.cif", csys)
278
+ write(Path(out_path) / f"{sysname}+{i + 1}{Path(molecule).stem}.cif", csys)
278
279
  else:
280
+ # Things are bad, maybe geomatry optimisation saves us
279
281
  print(f"Failed to insert particle {i + 1} after {ntries} tries")
280
- optimize_geometry(
281
- f"{sysname}+{i + 1}{Path(molecule).stem}.cif", device, arch, model, fmax
282
+ _ = optimize_geometry(
283
+ f"{sysname}+{i + 1}{Path(molecule).stem}.cif",
284
+ device,
285
+ arch,
286
+ model,
287
+ fmax,
288
+ out_path,
282
289
  )
290
+ energy_final = e
283
291
 
284
292
  # Perform final geometry optimization if requested
285
293
  if geometry:
286
- optimize_geometry(
287
- f"{sysname}+{nmols}{Path(molecule).stem}.cif", device, arch, model, fmax
294
+ energy_final = optimize_geometry(
295
+ f"{sysname}+{nmols}{Path(molecule).stem}.cif",
296
+ device,
297
+ arch,
298
+ model,
299
+ fmax,
300
+ out_path,
288
301
  )
302
+ return energy_final
289
303
 
290
304
 
291
305
  def load_molecule(molecule: str):
@@ -299,25 +313,24 @@ def load_molecule(molecule: str):
299
313
  def get_insertion_position(
300
314
  where: str,
301
315
  center: tuple[float, float, float],
302
- cell: list[float],
303
- a: float,
304
- b: float,
305
- c: float,
306
- radius: float | None,
307
- height: float | None,
316
+ a: float = None,
317
+ b: float = None,
318
+ c: float = None,
319
+ radius: float = None,
320
+ height: float = None,
308
321
  ) -> tuple[float, float, float]:
309
322
  """Get a random insertion position based on the region."""
310
323
  if where == "sphere":
311
324
  return random_point_in_sphere(center, radius)
312
325
  if where == "box":
313
- return random_point_in_box(center, cell[0] * a, cell[1] * b, cell[2] * c)
326
+ return random_point_in_box(center, a, b, c)
314
327
  if where == "ellipsoid":
315
- return random_point_in_ellipsoid(center, cell[0] * a, cell[1] * b, cell[2] * c)
328
+ return random_point_in_ellipsoid(center, a, b, c)
316
329
  if where in ["cylinderZ", "cylinderY", "cylinderX"]:
317
330
  axis = where[-1].lower()
318
- return random_point_in_cylinder(center, radius, cell[2] * height, axis)
331
+ return random_point_in_cylinder(center, radius, height, axis)
319
332
  # now is anywhere
320
- return random.random(3) * [a, b, c] * cell
333
+ return random.random(3) * [a, b, c]
321
334
 
322
335
 
323
336
  def rotate_molecule(mol):
@@ -330,16 +343,21 @@ def rotate_molecule(mol):
330
343
 
331
344
 
332
345
  def optimize_geometry(
333
- struct_path: str, device: str, arch: str, model: str, fmax: float
346
+ struct_path: str,
347
+ device: str,
348
+ arch: str,
349
+ model: str,
350
+ fmax: float,
351
+ out_path: str = ".",
334
352
  ) -> float:
335
353
  """Optimize the geometry of a structure."""
336
354
  geo = GeomOpt(
337
355
  struct_path=struct_path,
338
356
  device=device,
339
357
  fmax=fmax,
340
- calc_kwargs={"model_paths": model, "default_dtype": "float64"},
358
+ calc_kwargs={"model_paths": model},
341
359
  filter_kwargs={"hydrostatic_strain": True},
342
360
  )
343
361
  geo.run()
344
- write(f"{struct_path}-opt.cif", geo.struct)
362
+ write(Path(out_path) / f"{Path(struct_path).stem}-opt.cif", geo.struct)
345
363
  return geo.struct.get_potential_energy()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pack-mm"
3
- version = "0.0.15"
3
+ version = "0.0.20"
4
4
  description = "packing materials and molecules in boxes using for machine learnt interatomic potentials"
5
5
  authors = [
6
6
  { name = "Alin M. Elena" },
@@ -44,6 +44,7 @@ docs = [
44
44
  "furo<2025.0.0,>=2024.1.29",
45
45
  "jupyter>=1.1.1",
46
46
  "markupsafe<2.1",
47
+ "sphinx-immaterial",
47
48
  "nbsphinx>=0.9.6",
48
49
  "numpydoc<2.0.0,>=1.6.0",
49
50
  "sphinx<9.0.0,>=8.0.2",
@@ -136,3 +137,7 @@ default-groups = [
136
137
  "docs",
137
138
  "pre-commit",
138
139
  ]
140
+
141
+ [tool.uv.sources.sphinx-immaterial]
142
+ git = "https://github.com/jbms/sphinx-immaterial.git"
143
+ rev = "main"
@@ -0,0 +1,178 @@
1
+ """Test cli for core."""
2
+
3
+ # -*- coding: utf-8 -*-
4
+ # Author; alin m elena, alin@elena.re
5
+ # Contribs;
6
+ # Date: 22-02-2025
7
+ # ©alin m elena,
8
+ from __future__ import annotations
9
+
10
+ from ase import Atoms
11
+ from ase.build import molecule as build_molecule
12
+ from ase.io import write
13
+ import numpy as np
14
+ from numpy import random
15
+ import pytest
16
+
17
+ from pack_mm.core.core import (
18
+ get_insertion_position,
19
+ load_molecule,
20
+ optimize_geometry,
21
+ pack_molecules,
22
+ random_point_in_box,
23
+ random_point_in_cylinder,
24
+ random_point_in_ellipsoid,
25
+ random_point_in_sphere,
26
+ rotate_molecule,
27
+ validate_value,
28
+ )
29
+
30
+
31
+ # Set a fixed seed for reproducibility in tests
32
+ @pytest.fixture(autouse=True)
33
+ def set_random_seed():
34
+ """Set random seed."""
35
+ random.seed(2042)
36
+
37
+
38
+ def test_random_point_in_sphere():
39
+ """Test point in sphere."""
40
+ center = (0, 0, 0)
41
+ radius = 10.0
42
+ point = random_point_in_sphere(center, radius)
43
+ assert len(point) == 3
44
+ distance = np.linalg.norm(np.array(point) - np.array(center))
45
+ assert distance <= radius
46
+
47
+
48
+ def test_random_point_in_ellipsoid():
49
+ """Test point in ellipsoid."""
50
+ center = (0, 0, 0)
51
+ a, b, c = 1.0, 2.0, 3.0
52
+ point = random_point_in_ellipsoid(center, a, b, c)
53
+ assert len(point) == 3
54
+ x, y, z = point
55
+ assert (x**2 / a**2) + (y**2 / b**2) + (z**2 / c**2) <= 1.0
56
+
57
+
58
+ def test_random_point_in_box():
59
+ """Test point in box."""
60
+ center = (0, 0, 0)
61
+ a, b, c = 1.0, 2.0, 3.0
62
+ point = random_point_in_box(center, a, b, c)
63
+ assert len(point) == 3
64
+ x, y, z = point
65
+ assert center[0] - a * 0.5 <= x <= center[0] + a * 0.5
66
+ assert center[1] - b * 0.5 <= y <= center[1] + b * 0.5
67
+ assert center[2] - c * 0.5 <= z <= center[2] + c * 0.5
68
+
69
+
70
+ def test_random_point_in_cylinder():
71
+ """Test point in cylinder."""
72
+ center = (0, 0, 0)
73
+ radius = 1.0
74
+ height = 2.0
75
+ direction = "z"
76
+ point = random_point_in_cylinder(center, radius, height, direction)
77
+ assert len(point) == 3
78
+ x, y, z = point
79
+ assert x**2 + y**2 <= radius**2
80
+ assert center[2] - height * 0.5 <= z <= center[2] + height * 0.5
81
+
82
+
83
+ def test_validate_value_positive():
84
+ """Test point in test value."""
85
+ validate_value("test_value", 1.0) # Should not raise an exception
86
+
87
+
88
+ def test_validate_value_negative():
89
+ """Test point in test value."""
90
+ with pytest.raises(Exception, match="Invalid test_value, needs to be positive"):
91
+ validate_value("test_value", -1.0)
92
+
93
+
94
+ def test_load_molecule_from_file(tmp_path):
95
+ """Test point in load molecule."""
96
+ molecule = build_molecule("H2O")
97
+ molecule_file = tmp_path / "water.xyz"
98
+ molecule.write(molecule_file)
99
+ loaded_molecule = load_molecule(str(molecule_file))
100
+ assert isinstance(loaded_molecule, Atoms)
101
+ assert len(loaded_molecule) == 3 # H2O has 3 atoms
102
+
103
+
104
+ def test_load_molecule_from_name():
105
+ """Test point in load molecule."""
106
+ molecule = load_molecule("H2O")
107
+ assert isinstance(molecule, Atoms)
108
+ assert len(molecule) == 3 # H2O has 3 atoms
109
+
110
+
111
+ def test_get_insertion_position_sphere():
112
+ """Test point in sphere."""
113
+ center = (0, 0, 0)
114
+ radius = 10.0
115
+ point = get_insertion_position("sphere", center, radius=radius)
116
+ assert len(point) == 3
117
+ distance = np.linalg.norm(np.array(point) - np.array(center))
118
+ assert distance <= radius
119
+
120
+
121
+ def test_rotate_molecule():
122
+ """Test rotate molecule."""
123
+ molecule = build_molecule("H2O")
124
+ rotated_molecule = rotate_molecule(molecule)
125
+ assert isinstance(rotated_molecule, Atoms)
126
+ assert len(rotated_molecule) == 3 # H2O has 3 atoms
127
+
128
+
129
+ def test_optimize_geometry(tmp_path):
130
+ """Test go."""
131
+ # Create a temporary structure file
132
+ molecule = build_molecule("H2O")
133
+ molecule.set_cell([10, 10, 10])
134
+ molecule.set_pbc([True, True, True])
135
+ structure_file = tmp_path / "water.cif"
136
+ write(structure_file, molecule)
137
+ optimized_energy = optimize_geometry(
138
+ str(structure_file),
139
+ device="cpu",
140
+ arch="mace_mp",
141
+ model="medium-omat-0",
142
+ fmax=0.01,
143
+ )
144
+ assert optimized_energy == pytest.approx(-13.759273983276572, abs=1.0e-8)
145
+
146
+
147
+ # Test pack_molecules with a simple case
148
+ def test_pack_molecules(tmp_path):
149
+ """Test pack molecule."""
150
+ # Create a temporary system file
151
+ system = Atoms(
152
+ "Ca", positions=[(5.0, 5.0, 5.0)], cell=[10, 10, 10], pbc=[True, True, True]
153
+ )
154
+ system_file = tmp_path / "system.cif"
155
+ write(system_file, system)
156
+
157
+ # Test packing molecules
158
+ e = pack_molecules(
159
+ system=str(system_file),
160
+ molecule="H2O",
161
+ nmols=2,
162
+ arch="mace_mp",
163
+ model="medium-omat-0",
164
+ device="cpu",
165
+ where="sphere",
166
+ center=(5.0, 5.0, 5.0),
167
+ radius=5.0,
168
+ seed=2042,
169
+ temperature=300,
170
+ ntries=10,
171
+ geometry=False,
172
+ fmax=0.1,
173
+ out_path=tmp_path,
174
+ )
175
+
176
+ assert (tmp_path / "system+1H2O.cif").exists()
177
+ assert (tmp_path / "system+2H2O.cif").exists()
178
+ assert e == pytest.approx(-28.251229837533085, abs=1.0e-6)
@@ -3,7 +3,7 @@
3
3
  # Author; alin m elena, alin@elena.re
4
4
  # Contribs;
5
5
  # Date: 22-02-2025
6
- # ©alin m elena, GPL v3 https://www.gnu.org/licenses/gpl-3.0.en.html
6
+ # ©alin m elena,
7
7
  from __future__ import annotations
8
8
 
9
9
  from typer.testing import CliRunner
@@ -88,6 +88,13 @@ def test_packmm_mlip():
88
88
  assert "device='cuda'" in strip_ansi_codes(result.output)
89
89
 
90
90
 
91
+ def test_packmm_out_path():
92
+ """Check out_path."""
93
+ result = runner.invoke(app, ["--out-path", "out"])
94
+ assert result.exit_code == 0
95
+ assert "out_path='out'" in strip_ansi_codes(result.output)
96
+
97
+
91
98
  def test_packmm_custom_box_dimensions():
92
99
  """Check box."""
93
100
  result = runner.invoke(app, ["--a", "30.0", "--b", "30.0", "--c", "30.0"])
pack_mm-0.0.15/PKG-INFO DELETED
@@ -1,36 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: pack-mm
3
- Version: 0.0.15
4
- Summary: packing materials and molecules in boxes using for machine learnt interatomic potentials
5
- Author: Alin M. Elena
6
- Classifier: Programming Language :: Python
7
- Classifier: Programming Language :: Python :: 3.10
8
- Classifier: Programming Language :: Python :: 3.11
9
- Classifier: Programming Language :: Python :: 3.12
10
- Classifier: Intended Audience :: Science/Research
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Natural Language :: English
13
- Classifier: Development Status :: 3 - Alpha
14
- Project-URL: Repository, https://github.com/ddmms/pack-mm/
15
- Project-URL: Documentation, https://ddmms.github.io/pack-mm/
16
- Requires-Python: >=3.10
17
- Requires-Dist: janus-core>=0.7.2
18
- Requires-Dist: typer<1.0.0,>=0.12.5
19
- Requires-Dist: typer-config<2.0.0,>=1.4.2
20
- Description-Content-Type: text/markdown
21
-
22
- # pack materials and molecules
23
-
24
- [![Python versions][python-badge]][python-link]
25
- [![Build Status][ci-badge]][ci-link]
26
- [![Coverage Status][cov-badge]][cov-link]
27
- [![License][license-badge]][license-link]
28
-
29
- [python-badge]: https://img.shields.io/pypi/pyversions/pack-mm.svg
30
- [python-link]: https://pypi.org/project/pack-mm/
31
- [ci-badge]: https://github.com/ddmms/pack-mm/actions/workflows/build.yml/badge.svg?branch=main
32
- [ci-link]: https://github.com/ddmms/pack-mm/actions
33
- [cov-badge]: https://coveralls.io/repos/github/ddmms/pack-mm/badge.svg?branch=main
34
- [cov-link]: https://coveralls.io/github/ddmms/pack-mm?branch=main
35
- [license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
36
- [license-link]: https://opensource.org/license/MIT
pack_mm-0.0.15/README.md DELETED
@@ -1,15 +0,0 @@
1
- # pack materials and molecules
2
-
3
- [![Python versions][python-badge]][python-link]
4
- [![Build Status][ci-badge]][ci-link]
5
- [![Coverage Status][cov-badge]][cov-link]
6
- [![License][license-badge]][license-link]
7
-
8
- [python-badge]: https://img.shields.io/pypi/pyversions/pack-mm.svg
9
- [python-link]: https://pypi.org/project/pack-mm/
10
- [ci-badge]: https://github.com/ddmms/pack-mm/actions/workflows/build.yml/badge.svg?branch=main
11
- [ci-link]: https://github.com/ddmms/pack-mm/actions
12
- [cov-badge]: https://coveralls.io/repos/github/ddmms/pack-mm/badge.svg?branch=main
13
- [cov-link]: https://coveralls.io/github/ddmms/pack-mm?branch=main
14
- [license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
15
- [license-link]: https://opensource.org/license/MIT
File without changes
File without changes
File without changes
File without changes