mdinterface 1.5.2__tar.gz → 1.5.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.
- {mdinterface-1.5.2/mdinterface.egg-info → mdinterface-1.5.4}/PKG-INFO +9 -11
- {mdinterface-1.5.2 → mdinterface-1.5.4}/README.md +4 -6
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/__init__.py +2 -2
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/builder.py +1 -1
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/core/specie.py +3 -2
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/core/topology.py +3 -2
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/io/lammpswriter.py +51 -18
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/simulationbox.py +9 -3
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/map.py +9 -19
- {mdinterface-1.5.2 → mdinterface-1.5.4/mdinterface.egg-info}/PKG-INFO +9 -11
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface.egg-info/SOURCES.txt +2 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface.egg-info/top_level.txt +1 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/pyproject.toml +4 -4
- mdinterface-1.5.4/scripts/extract_changelog_summary.py +78 -0
- mdinterface-1.5.4/tests/test_changelog_summary.py +91 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/LICENSE +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/MANIFEST.in +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/assets/mdinterface.png +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/api/database.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/api/externals.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/api/io.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/api/polymer.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/api/simcell.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/api/specie.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/assets/mdinterface.png +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/guide/database.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/guide/logging.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/guide/polymer.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/guide/simcell.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/guide/specie.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/index.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/installation.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/quickstart.md +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/docs/requirements.txt +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/electrode_interface.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/legacy/make_POSCAR.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/legacy/make_box.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/legacy/make_polymer.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/legacy/make_solvent_box.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/legacy/make_specie_ligpargen_resp.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/multilayer.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/multisolvent_box.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/polymer/polymer_piperion.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/sandwich_from_traj.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/examples/solvent_box.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/box.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/continuum2sim.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/polymerize.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/snippets.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/build/solvent.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/config.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/core/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/core/polymer.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/database/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/database/graphene.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/database/ions.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/database/metals.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/database/molecules.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/database/nobles.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/aimd.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/ase.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/ligpargen.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/obabel.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/optimization.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/externals/pyscf.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/io/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/io/gromacswriter.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/io/packmol.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/io/read.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/read/cp2ktraj.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/read/lammpstraj.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/read/read.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/read/trajectory.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/read/xyztraj.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/auxiliary.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/draw.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/graphs.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/logger.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/poisson.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/rings.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface/utils/units.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface.egg-info/dependency_links.txt +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/mdinterface.egg-info/requires.txt +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/requirements.txt +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/setup.cfg +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/setup.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/__init__.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/test_auxiliary.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/test_builder.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/test_database.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/test_solvent.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/test_specie.py +0 -0
- {mdinterface-1.5.2 → mdinterface-1.5.4}/tests/test_topology.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdinterface
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
4
4
|
Summary: Build Interface Systems for Molecular Dynamics Simulations
|
|
5
5
|
Author-email: Fabrice Roncoroni <fabrice.roncoroni@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://
|
|
8
|
-
Project-URL: Repository, https://
|
|
9
|
-
Project-URL: Documentation, https://
|
|
10
|
-
Project-URL: Bug Tracker, https://
|
|
7
|
+
Project-URL: Homepage, https://github.com/roncofaber/mdinterface
|
|
8
|
+
Project-URL: Repository, https://github.com/roncofaber/mdinterface.git
|
|
9
|
+
Project-URL: Documentation, https://roncofaber.github.io/mdinterface
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/roncofaber/mdinterface/issues
|
|
11
11
|
Keywords: molecular dynamics,simulation,interface,chemistry,materials science
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -41,12 +41,10 @@ Requires-Dist: pymbxas; extra == "all"
|
|
|
41
41
|
Requires-Dist: fairchem-core; extra == "all"
|
|
42
42
|
Dynamic: license-file
|
|
43
43
|
|
|
44
|
-
<
|
|
45
|
-
<img src="./assets/mdinterface.png" alt="Logo" width="
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
44
|
+
<h1>
|
|
45
|
+
<img src="./assets/mdinterface.png" alt="Logo" width="60" style="vertical-align: middle; margin-right: 10px;">
|
|
46
|
+
mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
47
|
+
</h1>
|
|
50
48
|
|
|
51
49
|
[](https://pypi.org/project/mdinterface/) [](https://github.com/roncofaber/mdinterface) [](https://roncofaber.github.io/mdinterface)
|
|
52
50
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
<
|
|
2
|
-
<img src="./assets/mdinterface.png" alt="Logo" width="
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
</div>
|
|
6
|
-
</div>
|
|
1
|
+
<h1>
|
|
2
|
+
<img src="./assets/mdinterface.png" alt="Logo" width="60" style="vertical-align: middle; margin-right: 10px;">
|
|
3
|
+
mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
4
|
+
</h1>
|
|
7
5
|
|
|
8
6
|
[](https://pypi.org/project/mdinterface/) [](https://github.com/roncofaber/mdinterface) [](https://roncofaber.github.io/mdinterface)
|
|
9
7
|
|
|
@@ -6,8 +6,8 @@ Initially developed to construct electrolyte/electrode interfaces, it is also we
|
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = '1.5.
|
|
10
|
-
__date__ = '
|
|
9
|
+
__version__ = '1.5.4'
|
|
10
|
+
__date__ = '12 Aug. 2026'
|
|
11
11
|
__author__ = 'Fabrice Roncoroni'
|
|
12
12
|
__all__ = ['SimulationBox', 'SimCell', 'BoxBuilder', "Specie", "Polymer"]
|
|
13
13
|
|
|
@@ -857,9 +857,10 @@ class Specie(object):
|
|
|
857
857
|
charges, atoms = calculate_RESP_charges(self, **respargs)
|
|
858
858
|
|
|
859
859
|
if assign:
|
|
860
|
-
self.atoms.set_positions(atoms.get_positions())
|
|
861
860
|
self.atoms.set_initial_charges(charges)
|
|
862
|
-
|
|
861
|
+
if method == "resp":
|
|
862
|
+
self.atoms.set_positions(atoms.get_positions())
|
|
863
|
+
|
|
863
864
|
return charges
|
|
864
865
|
|
|
865
866
|
def estimate_OPLSAA_parameters(self, charge=None):
|
|
@@ -249,13 +249,14 @@ class Dihedral(Topology):
|
|
|
249
249
|
return (self.values == other.values and
|
|
250
250
|
(self.symbols == other.symbols or self.symbols == other.symbols[::-1]))
|
|
251
251
|
|
|
252
|
-
def write(self, fout):
|
|
252
|
+
def write(self, fout, idx=None):
|
|
253
253
|
atype = "{}-{}-{}-{}".format(*self.symbols)
|
|
254
254
|
if self._values[-1] is not None:
|
|
255
255
|
value = "{:>7.4f} {:>7.4f} {:>7.4f} {:>7.4f} {:>7.4f}".format(*self.values)
|
|
256
256
|
else:
|
|
257
257
|
value = "{:>7.4f} {:>7.4f} {:>7.4f} {:>7.4f}".format(*self.values[:-1])
|
|
258
|
-
fout.write("{:>5} {} # {:<8} | {}\n".format(
|
|
258
|
+
fout.write("{:>5} {} # {:<8} | {}\n".format(
|
|
259
|
+
idx if idx is not None else self.id, value, atype, self.resname))
|
|
259
260
|
|
|
260
261
|
def __bool__(self):
|
|
261
262
|
return any([bool(val) for val in self.values])
|
|
@@ -25,6 +25,16 @@ btype_sections = {'bond':'Bonds', 'angle':'Angles',
|
|
|
25
25
|
'dihedral':'Dihedrals', 'improper':'Impropers'}
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def _renumber_bonded_types(raw_types):
|
|
29
|
+
"""
|
|
30
|
+
Map raw per-item bond/angle/dihedral/improper type ids -- which may be
|
|
31
|
+
sparse or non-contiguous (e.g. when a Specie template is reused across
|
|
32
|
+
several layers) -- to a contiguous 1..N range ordered by numeric value.
|
|
33
|
+
"""
|
|
34
|
+
unique_sorted = sorted({int(t) for t in raw_types})
|
|
35
|
+
return {t: i + 1 for i, t in enumerate(unique_sorted)}
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
class DATAWriter(base.WriterBase):
|
|
29
39
|
"""Write out the current time step as a LAMMPS DATA file.
|
|
30
40
|
|
|
@@ -96,9 +106,10 @@ class DATAWriter(base.WriterBase):
|
|
|
96
106
|
moltags = atoms.resindices
|
|
97
107
|
m_l = len(str(moltags.max()))+1
|
|
98
108
|
|
|
109
|
+
coordinates = atoms.positions.copy()
|
|
99
110
|
if self.convert_units:
|
|
100
|
-
coordinates = self.convert_pos_to_native(
|
|
101
|
-
|
|
111
|
+
coordinates = self.convert_pos_to_native(coordinates, inplace=False)
|
|
112
|
+
|
|
102
113
|
b_l = len(str(int(coordinates.max()))) + 1
|
|
103
114
|
b_t = b_l + 6
|
|
104
115
|
|
|
@@ -160,9 +171,14 @@ class DATAWriter(base.WriterBase):
|
|
|
160
171
|
self.f.write('\n')
|
|
161
172
|
self.f.write('{}\n'.format(btype_sections[bonds.btype]))
|
|
162
173
|
self.f.write('\n')
|
|
174
|
+
try:
|
|
175
|
+
remap = _renumber_bonded_types(bond.type for bond in bonds)
|
|
176
|
+
except (TypeError, ValueError):
|
|
177
|
+
remap = None
|
|
163
178
|
for bond, i in zip(bonds, range(1, len(bonds)+1)):
|
|
164
179
|
try:
|
|
165
|
-
|
|
180
|
+
btype_id = remap[int(bond.type)] if remap is not None else int(bond.type)
|
|
181
|
+
self.f.write('{:d} {:d} '.format(i, btype_id)+\
|
|
166
182
|
' '.join((bond.atoms.indices + 1).astype(str))+'\n')
|
|
167
183
|
except TypeError:
|
|
168
184
|
errmsg = (f"LAMMPS DATAWriter: Trying to write bond, but bond "
|
|
@@ -173,9 +189,9 @@ class DATAWriter(base.WriterBase):
|
|
|
173
189
|
"""Convert dimensions to triclinic vectors, convert lengths to native
|
|
174
190
|
units and then write the dimensions section
|
|
175
191
|
"""
|
|
192
|
+
triv = mdamath.triclinic_vectors(dimensions)
|
|
176
193
|
if self.convert_units:
|
|
177
|
-
triv = self.convert_pos_to_native(
|
|
178
|
-
dimensions),inplace=False)
|
|
194
|
+
triv = self.convert_pos_to_native(triv, inplace=False)
|
|
179
195
|
self.f.write('\n')
|
|
180
196
|
self.f.write('{:f} {:f} xlo xhi\n'.format(0., triv[0][0]))
|
|
181
197
|
self.f.write('{:f} {:f} ylo yhi\n'.format(0., triv[1][1]))
|
|
@@ -336,18 +352,20 @@ def write_lammps_coefficients(
|
|
|
336
352
|
# Write Pair Coefficients
|
|
337
353
|
fout.write("Pair Coeffs\n\n")
|
|
338
354
|
|
|
339
|
-
|
|
355
|
+
present_types = list(np.unique(system.atoms.types))
|
|
340
356
|
atom_types = sorted_attributes.get("atoms", [])
|
|
357
|
+
written = set()
|
|
341
358
|
for atom in atom_types:
|
|
342
|
-
if atom.extended_label not in
|
|
359
|
+
if atom.extended_label not in present_types or atom.extended_label in written:
|
|
343
360
|
continue
|
|
361
|
+
written.add(atom.extended_label)
|
|
362
|
+
idx = present_types.index(atom.extended_label) + 1
|
|
344
363
|
|
|
345
364
|
eps = atom.eps if atom.eps is not None else 0
|
|
346
365
|
sig = atom.sig if atom.sig is not None else 0
|
|
347
366
|
|
|
348
367
|
fout.write("{:>5} {:>12.8f} {:>12.8f} # {}\n".format(
|
|
349
368
|
idx, eps, sig, atom.extended_label))
|
|
350
|
-
idx += 1
|
|
351
369
|
|
|
352
370
|
# Write Bond Coefficients
|
|
353
371
|
bond_types = sorted_attributes.get("bonds", [])
|
|
@@ -355,9 +373,13 @@ def write_lammps_coefficients(
|
|
|
355
373
|
fout.write("\n")
|
|
356
374
|
fout.write("Bond Coeffs\n\n")
|
|
357
375
|
|
|
376
|
+
bond_remap = _renumber_bonded_types(system.bonds.types()) if len(system.bonds) else {}
|
|
377
|
+
written_bonds = set()
|
|
358
378
|
for bond in bond_types:
|
|
359
|
-
if bond.id not in
|
|
379
|
+
if int(bond.id) not in bond_remap or bond.id in written_bonds:
|
|
360
380
|
continue
|
|
381
|
+
written_bonds.add(bond.id)
|
|
382
|
+
idx = bond_remap[int(bond.id)]
|
|
361
383
|
|
|
362
384
|
kr = bond.kr if bond.kr is not None else 0
|
|
363
385
|
r0 = bond.r0 if bond.r0 is not None else 0
|
|
@@ -365,7 +387,7 @@ def write_lammps_coefficients(
|
|
|
365
387
|
btype = "{}-{}".format(*bond.symbols)
|
|
366
388
|
|
|
367
389
|
fout.write("{:>5} {:>10.6f} {:>10.6f} # {:<5} | {}\n".format(
|
|
368
|
-
|
|
390
|
+
idx, kr, r0, btype, bond.resname))
|
|
369
391
|
|
|
370
392
|
# Write Angle Coefficients
|
|
371
393
|
angle_types = sorted_attributes.get("angles", [])
|
|
@@ -373,9 +395,13 @@ def write_lammps_coefficients(
|
|
|
373
395
|
fout.write("\n")
|
|
374
396
|
fout.write("Angle Coeffs\n\n")
|
|
375
397
|
|
|
398
|
+
angle_remap = _renumber_bonded_types(system.angles.types()) if len(system.angles) else {}
|
|
399
|
+
written_angles = set()
|
|
376
400
|
for angle in angle_types:
|
|
377
|
-
if angle.id not in
|
|
401
|
+
if int(angle.id) not in angle_remap or angle.id in written_angles:
|
|
378
402
|
continue
|
|
403
|
+
written_angles.add(angle.id)
|
|
404
|
+
idx = angle_remap[int(angle.id)]
|
|
379
405
|
|
|
380
406
|
kr = angle.kr if angle.kr is not None else 0
|
|
381
407
|
theta0 = angle.theta0 if angle.theta0 is not None else 0
|
|
@@ -383,7 +409,7 @@ def write_lammps_coefficients(
|
|
|
383
409
|
atype = "{}-{}-{}".format(*angle.symbols)
|
|
384
410
|
|
|
385
411
|
fout.write("{:>5} {:>10.6f} {:>10.6f} # {:<8} | {}\n".format(
|
|
386
|
-
|
|
412
|
+
idx, kr, theta0, atype, angle.resname))
|
|
387
413
|
|
|
388
414
|
# Write Dihedral Coefficients
|
|
389
415
|
dihedral_types = sorted_attributes.get("dihedrals", [])
|
|
@@ -391,26 +417,33 @@ def write_lammps_coefficients(
|
|
|
391
417
|
fout.write("\n")
|
|
392
418
|
fout.write("Dihedral Coeffs\n\n")
|
|
393
419
|
|
|
420
|
+
dihedral_remap = _renumber_bonded_types(system.dihedrals.types()) if len(system.dihedrals) else {}
|
|
421
|
+
written_dihedrals = set()
|
|
394
422
|
for dihedral in dihedral_types:
|
|
395
|
-
if dihedral.id not in
|
|
423
|
+
if int(dihedral.id) not in dihedral_remap or dihedral.id in written_dihedrals:
|
|
396
424
|
continue
|
|
425
|
+
written_dihedrals.add(dihedral.id)
|
|
397
426
|
|
|
398
|
-
dihedral.write(fout)
|
|
427
|
+
dihedral.write(fout, idx=dihedral_remap[int(dihedral.id)])
|
|
399
428
|
|
|
400
429
|
# Write Improper Coefficients
|
|
401
430
|
improper_types = sorted_attributes.get("impropers", [])
|
|
402
431
|
if improper_types and system.impropers.types():
|
|
403
432
|
fout.write("\n")
|
|
404
433
|
fout.write("Improper Coeffs\n\n")
|
|
405
|
-
|
|
434
|
+
|
|
435
|
+
improper_remap = _renumber_bonded_types(system.impropers.types()) if len(system.impropers) else {}
|
|
436
|
+
written_impropers = set()
|
|
406
437
|
for improper in improper_types:
|
|
407
|
-
if improper.id not in
|
|
438
|
+
if int(improper.id) not in improper_remap or improper.id in written_impropers:
|
|
408
439
|
continue
|
|
440
|
+
written_impropers.add(improper.id)
|
|
441
|
+
idx = improper_remap[int(improper.id)]
|
|
409
442
|
|
|
410
|
-
atype = "{}".format(*improper.symbols)
|
|
443
|
+
atype = "{}-{}-{}-{}".format(*improper.symbols)
|
|
411
444
|
value = "{:>7.4f} {:>2d} {:>2d}".format(*improper.values)
|
|
412
445
|
|
|
413
|
-
fout.write("{:>5} {} # {:<2} | {}\n".format(
|
|
446
|
+
fout.write("{:>5} {} # {:<2} | {}\n".format(idx, value, atype, improper.resname))
|
|
414
447
|
|
|
415
448
|
fout.write("\n")
|
|
416
449
|
|
|
@@ -22,7 +22,6 @@ import numpy as np
|
|
|
22
22
|
|
|
23
23
|
import shutil
|
|
24
24
|
import warnings
|
|
25
|
-
warnings.filterwarnings('ignore')
|
|
26
25
|
|
|
27
26
|
#%%
|
|
28
27
|
|
|
@@ -56,7 +55,14 @@ class SimulationBox():
|
|
|
56
55
|
def __init__(self, solvent: Optional[Any] = None, solute: Optional[Union[Any, List[Any]]] = None,
|
|
57
56
|
interface: Optional[Any] = None, enderface: Optional[Any] = None,
|
|
58
57
|
miderface: Optional[Any] = None) -> None:
|
|
59
|
-
|
|
58
|
+
|
|
59
|
+
warnings.warn(
|
|
60
|
+
"SimulationBox is deprecated and will be removed in a future version. "
|
|
61
|
+
"Use SimCell instead.",
|
|
62
|
+
DeprecationWarning,
|
|
63
|
+
stacklevel=2,
|
|
64
|
+
)
|
|
65
|
+
|
|
60
66
|
# start species
|
|
61
67
|
self._setup_species(solvent, solute, interface, enderface, miderface)
|
|
62
68
|
|
|
@@ -367,7 +373,7 @@ class SimulationBox():
|
|
|
367
373
|
ion_pos = layer["ion_pos"]
|
|
368
374
|
|
|
369
375
|
# make solvent box
|
|
370
|
-
solvent = make_solvent_box(self.species, self.
|
|
376
|
+
solvent = make_solvent_box(self.species, self._solvent, self._solute,
|
|
371
377
|
[xsize, ysize, zsize], solv_rho,
|
|
372
378
|
nions, concentration, conmodel, ion_pos, nsolvent)
|
|
373
379
|
|
|
@@ -140,7 +140,7 @@ def map_dihedrals(dihedrals):
|
|
|
140
140
|
def map_impropers(impropers):
|
|
141
141
|
|
|
142
142
|
if impropers is None:
|
|
143
|
-
return
|
|
143
|
+
return [], {}, np.array([])
|
|
144
144
|
|
|
145
145
|
improper_type_ids = []
|
|
146
146
|
type_id = 0
|
|
@@ -267,49 +267,39 @@ def generate_missing_interactions(nas, interaction_type):
|
|
|
267
267
|
continue
|
|
268
268
|
|
|
269
269
|
for mss_interaction in mss_interactions:
|
|
270
|
-
found_interaction = False
|
|
271
270
|
stypes = tuple(nas._smap[ii] for ii in mss_interaction)
|
|
272
271
|
stypes_rev = stypes[::-1]
|
|
273
|
-
|
|
272
|
+
|
|
274
273
|
# topology attribute already existing in map
|
|
275
274
|
if stypes in interaction_type_map or stypes_rev in interaction_type_map:
|
|
276
|
-
if found_interaction:
|
|
277
|
-
raise ValueError(f"More than one possible {interaction_type[:-1]} found, abort!")
|
|
278
|
-
|
|
279
275
|
if stypes in interaction_type_map:
|
|
280
276
|
ninteraction = interaction_type_map[stypes].copy()
|
|
281
|
-
|
|
282
277
|
else:
|
|
283
278
|
ninteraction = interaction_type_map[stypes_rev].copy()
|
|
284
279
|
mss_interaction = mss_interaction[::-1]
|
|
285
|
-
|
|
280
|
+
|
|
286
281
|
a1, a2, *rest = mss_interaction
|
|
287
|
-
|
|
282
|
+
|
|
288
283
|
ninteraction._a1 = a1
|
|
289
284
|
ninteraction._a2 = a2
|
|
290
|
-
|
|
285
|
+
|
|
291
286
|
if num_atoms > 2:
|
|
292
287
|
ninteraction._a3 = rest[0]
|
|
293
288
|
if num_atoms > 3:
|
|
294
289
|
ninteraction._a4 = rest[1]
|
|
295
290
|
new_interactions.append(ninteraction)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
# new topology attribute
|
|
291
|
+
|
|
292
|
+
# new topology attribute — type not yet defined, create bare object
|
|
299
293
|
else:
|
|
300
|
-
if found_interaction:
|
|
301
|
-
raise ValueError(f"More than one possible {interaction_type[:-1]} found, abort!")
|
|
302
|
-
|
|
303
294
|
a1, a2, *rest = mss_interaction
|
|
304
|
-
|
|
295
|
+
|
|
305
296
|
if num_atoms == 2:
|
|
306
297
|
ninteraction = Bond(a1, a2)
|
|
307
298
|
elif num_atoms == 3:
|
|
308
299
|
ninteraction = Angle(a1, a2, rest[0])
|
|
309
300
|
elif num_atoms == 4:
|
|
310
301
|
ninteraction = Dihedral(a1, a2, rest[0], rest[1])
|
|
311
|
-
|
|
302
|
+
|
|
312
303
|
new_interactions.append(ninteraction)
|
|
313
|
-
found_interaction = True
|
|
314
304
|
|
|
315
305
|
return new_interactions
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdinterface
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
4
4
|
Summary: Build Interface Systems for Molecular Dynamics Simulations
|
|
5
5
|
Author-email: Fabrice Roncoroni <fabrice.roncoroni@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://
|
|
8
|
-
Project-URL: Repository, https://
|
|
9
|
-
Project-URL: Documentation, https://
|
|
10
|
-
Project-URL: Bug Tracker, https://
|
|
7
|
+
Project-URL: Homepage, https://github.com/roncofaber/mdinterface
|
|
8
|
+
Project-URL: Repository, https://github.com/roncofaber/mdinterface.git
|
|
9
|
+
Project-URL: Documentation, https://roncofaber.github.io/mdinterface
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/roncofaber/mdinterface/issues
|
|
11
11
|
Keywords: molecular dynamics,simulation,interface,chemistry,materials science
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -41,12 +41,10 @@ Requires-Dist: pymbxas; extra == "all"
|
|
|
41
41
|
Requires-Dist: fairchem-core; extra == "all"
|
|
42
42
|
Dynamic: license-file
|
|
43
43
|
|
|
44
|
-
<
|
|
45
|
-
<img src="./assets/mdinterface.png" alt="Logo" width="
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
44
|
+
<h1>
|
|
45
|
+
<img src="./assets/mdinterface.png" alt="Logo" width="60" style="vertical-align: middle; margin-right: 10px;">
|
|
46
|
+
mdinterface: Build Interface Systems for Molecular Dynamics Simulations
|
|
47
|
+
</h1>
|
|
50
48
|
|
|
51
49
|
[](https://pypi.org/project/mdinterface/) [](https://github.com/roncofaber/mdinterface) [](https://roncofaber.github.io/mdinterface)
|
|
52
50
|
|
|
@@ -83,9 +83,11 @@ mdinterface/utils/map.py
|
|
|
83
83
|
mdinterface/utils/poisson.py
|
|
84
84
|
mdinterface/utils/rings.py
|
|
85
85
|
mdinterface/utils/units.py
|
|
86
|
+
scripts/extract_changelog_summary.py
|
|
86
87
|
tests/__init__.py
|
|
87
88
|
tests/test_auxiliary.py
|
|
88
89
|
tests/test_builder.py
|
|
90
|
+
tests/test_changelog_summary.py
|
|
89
91
|
tests/test_database.py
|
|
90
92
|
tests/test_solvent.py
|
|
91
93
|
tests/test_specie.py
|
|
@@ -38,10 +38,10 @@ dependencies = [
|
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
[project.urls]
|
|
41
|
-
Homepage = "https://
|
|
42
|
-
Repository = "https://
|
|
43
|
-
Documentation = "https://
|
|
44
|
-
"Bug Tracker" = "https://
|
|
41
|
+
Homepage = "https://github.com/roncofaber/mdinterface"
|
|
42
|
+
Repository = "https://github.com/roncofaber/mdinterface.git"
|
|
43
|
+
Documentation = "https://roncofaber.github.io/mdinterface"
|
|
44
|
+
"Bug Tracker" = "https://github.com/roncofaber/mdinterface/issues"
|
|
45
45
|
|
|
46
46
|
[project.optional-dependencies]
|
|
47
47
|
resp = [
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Extract a version's summary paragraph from CHANGELOG.md.
|
|
3
|
+
|
|
4
|
+
Usage: python scripts/extract_changelog_summary.py VERSION
|
|
5
|
+
|
|
6
|
+
Prints the summary paragraph to stdout and exits 0 on success. Exits 1 with
|
|
7
|
+
an error on stderr if the version's changelog header, or its summary
|
|
8
|
+
paragraph, is missing.
|
|
9
|
+
"""
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def extract_summary(changelog_text: str, version: str) -> str:
|
|
15
|
+
"""Return the summary paragraph for `## [version]` in changelog_text.
|
|
16
|
+
|
|
17
|
+
The summary is every non-blank line immediately following the header
|
|
18
|
+
(after skipping any blank lines right after it), stopping at the next
|
|
19
|
+
blank line or the next '## '/'### ' header — i.e. exactly one paragraph.
|
|
20
|
+
"""
|
|
21
|
+
header = f"## [{version}]"
|
|
22
|
+
lines = changelog_text.splitlines()
|
|
23
|
+
|
|
24
|
+
header_idx = None
|
|
25
|
+
for i, line in enumerate(lines):
|
|
26
|
+
if line.startswith(header):
|
|
27
|
+
header_idx = i
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
if header_idx is None:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
f"No changelog header found for version {version!r} "
|
|
33
|
+
f"(expected a line starting with {header!r})"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
i = header_idx + 1
|
|
37
|
+
while i < len(lines) and lines[i].strip() == "":
|
|
38
|
+
i += 1
|
|
39
|
+
|
|
40
|
+
summary_lines = []
|
|
41
|
+
while i < len(lines):
|
|
42
|
+
line = lines[i]
|
|
43
|
+
if line.strip() == "" or line.startswith("## ") or line.startswith("### "):
|
|
44
|
+
break
|
|
45
|
+
summary_lines.append(line)
|
|
46
|
+
i += 1
|
|
47
|
+
|
|
48
|
+
summary = "\n".join(summary_lines).strip()
|
|
49
|
+
if not summary:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"No summary paragraph found under {header!r} in CHANGELOG.md - "
|
|
52
|
+
"add one before the first '### ' group"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return summary
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def main(argv):
|
|
59
|
+
if len(argv) != 2:
|
|
60
|
+
print("Usage: extract_changelog_summary.py VERSION", file=sys.stderr)
|
|
61
|
+
return 1
|
|
62
|
+
|
|
63
|
+
version = argv[1]
|
|
64
|
+
changelog_path = Path(__file__).resolve().parent.parent / "CHANGELOG.md"
|
|
65
|
+
changelog_text = changelog_path.read_text()
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
summary = extract_summary(changelog_text, version)
|
|
69
|
+
except ValueError as exc:
|
|
70
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
print(summary)
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
sys.exit(main(sys.argv))
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
_SCRIPT_PATH = Path(__file__).resolve().parent.parent / "scripts" / "extract_changelog_summary.py"
|
|
7
|
+
_spec = importlib.util.spec_from_file_location("extract_changelog_summary", _SCRIPT_PATH)
|
|
8
|
+
_module = importlib.util.module_from_spec(_spec)
|
|
9
|
+
_spec.loader.exec_module(_module)
|
|
10
|
+
extract_summary = _module.extract_summary
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_extract_summary_found():
|
|
14
|
+
changelog = """# Changelog
|
|
15
|
+
|
|
16
|
+
## [Unreleased]
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## [1.5.4] - 2026-08-11
|
|
21
|
+
|
|
22
|
+
Fixes a silent LAMMPS output corruption bug.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Something
|
|
26
|
+
"""
|
|
27
|
+
assert extract_summary(changelog, "1.5.4") == "Fixes a silent LAMMPS output corruption bug."
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_extract_summary_multiline():
|
|
31
|
+
changelog = """## [1.5.4] - 2026-08-11
|
|
32
|
+
|
|
33
|
+
Fixes a silent LAMMPS output corruption bug affecting systems that reuse
|
|
34
|
+
the same Specie template across multiple layers.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Something
|
|
38
|
+
"""
|
|
39
|
+
expected = (
|
|
40
|
+
"Fixes a silent LAMMPS output corruption bug affecting systems that reuse\n"
|
|
41
|
+
"the same Specie template across multiple layers."
|
|
42
|
+
)
|
|
43
|
+
assert extract_summary(changelog, "1.5.4") == expected
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_missing_version_header_raises():
|
|
47
|
+
changelog = """## [1.5.3] - 2026-07-09
|
|
48
|
+
|
|
49
|
+
Some summary.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
- Something
|
|
53
|
+
"""
|
|
54
|
+
with pytest.raises(ValueError, match="No changelog header found"):
|
|
55
|
+
extract_summary(changelog, "1.5.4")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_missing_summary_paragraph_raises():
|
|
59
|
+
changelog = """## [1.5.4] - 2026-08-11
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
- Something
|
|
63
|
+
"""
|
|
64
|
+
with pytest.raises(ValueError, match="No summary paragraph found"):
|
|
65
|
+
extract_summary(changelog, "1.5.4")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_second_paragraph_not_included():
|
|
69
|
+
changelog = """## [1.5.4] - 2026-08-11
|
|
70
|
+
|
|
71
|
+
First paragraph only.
|
|
72
|
+
|
|
73
|
+
Second paragraph should not be included.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
- Something
|
|
77
|
+
"""
|
|
78
|
+
assert extract_summary(changelog, "1.5.4") == "First paragraph only."
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_header_prefix_does_not_match_longer_version():
|
|
82
|
+
# "## [1.5.4]" must not match when looking for "1.5" or "1.5.40"
|
|
83
|
+
changelog = """## [1.5.40] - 2026-08-11
|
|
84
|
+
|
|
85
|
+
Wrong version's summary.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
- Something
|
|
89
|
+
"""
|
|
90
|
+
with pytest.raises(ValueError, match="No changelog header found"):
|
|
91
|
+
extract_summary(changelog, "1.5.4")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|