Simulation Box¶
A Box defines the simulation cell: its shape, periodic boundary conditions, PBC, and the coordinate transforms you need to work safely with periodic systems.
You will mostly use Box for wrapping coordinates into the primary cell, wrap, Minimum-image distance vectors and distances, diff, dist, and Converting between absolute and fractional coordinates, make_fractional, make_absolute.
MolPy represents the box as a $3\times 3$ matrix whose columns are lattice vectors. From that matrix, MolPy can work with three common styles: FREE: no boundaries / no periodicity, ORTHOGONAL: rectangular, diagonal matrix, and TRICLINIC: general cell with tilts, off-diagonal terms.
1. Create boxes¶
Use factory constructors for common cells, or pass a matrix when you already have lattice vectors. Keep your unit system consistent with the coordinates you will store, Å, nm, etc..
import molpy as mp
import numpy as np
cubic = mp.Box.cubic(20.0)
ortho = mp.Box.orth([10.0, 20.0, 30.0])
# LAMMPS-style triclinic: lengths + tilts (xy, xz, yz)
tric = mp.Box.tric(lengths=[10.0, 12.0, 15.0], tilts=[1.0, 0.5, 0.2])
# Direct matrix construction (columns are lattice vectors)
matrix = np.array([[10.0, 1.0, 0.5], [0.0, 12.0, 0.2], [0.0, 0.0, 15.0]])
box_from_matrix = mp.Box(matrix=matrix)
{
"cubic_style": str(cubic.style),
"ortho_lengths": ortho.lengths.tolist(),
"tric_tilts": tric.tilts.tolist(),
"matrix": box_from_matrix.matrix,
}
{'cubic_style': 'Style.ORTHOGONAL',
'ortho_lengths': [10.0, 20.0, 30.0],
'tric_tilts': [1.0, 0.5, 0.2],
'matrix': array([[10. , 1. , 0.5],
[ 0. , 12. , 0.2],
[ 0. , 0. , 15. ]])}
2. Periodic boundary conditions¶
PBC controls whether coordinates wrap around the box on each axis. Common setups:
bulk system: periodic in x/y/z and slab/interface: periodic in x/y, non-periodic in z.
import molpy as mp
bulk = mp.Box.cubic(10.0)
slab = mp.Box.orth([20.0, 20.0, 50.0], pbc=[True, True, False])
{
"bulk_pbc": bulk.pbc.tolist(),
"slab_pbc": slab.pbc.tolist(),
"slab_periodic_z": slab.periodic_z,
}
{'bulk_pbc': [True, True, True],
'slab_pbc': [True, True, False],
'slab_periodic_z': np.False_}
3. Geometry and derived properties¶
Box exposes derived properties from its matrix representation. You’ll typically inspect:
style: FREE / ORTHOGONAL / TRICLINIC, lengths, volume, origin, bounds, and, triclinic only tilts, angles.
import molpy as mp
import numpy as np
ortho = mp.Box.orth([10.0, 12.0, 15.0])
tric = mp.Box.tric(lengths=[10.0, 12.0, 15.0], tilts=[1.0, 0.5, 0.2])
{
"ortho": {
"style": str(ortho.style),
"lengths": ortho.lengths.tolist(),
"volume": float(ortho.volume),
"origin": ortho.origin.tolist(),
"bounds": ortho.bounds.tolist(),
"matrix": ortho.matrix,
},
"tric": {
"style": str(tric.style),
"lengths": tric.lengths.tolist(),
"tilts": tric.tilts.tolist(),
"angles_deg": tric.angles.tolist(),
"volume": float(tric.volume),
"matrix": tric.matrix,
},
}
{'ortho': {'style': 'Style.ORTHOGONAL',
'lengths': [10.0, 12.0, 15.0],
'volume': 1800.000000000002,
'origin': [0.0, 0.0, 0.0],
'bounds': [[0.0, 0.0, 0.0], [10.0, 12.0, 15.0]],
'matrix': array([[10., 0., 0.],
[ 0., 12., 0.],
[ 0., 0., 15.]])},
'tric': {'style': 'Style.TRICLINIC',
'lengths': [10.0, 12.041594578792296, 15.00966355385756],
'tilts': [1.0, 0.5, 0.2],
'angles_deg': [89.0806427441657, 88.09101712126736, 85.23635830927383],
'volume': 1800.000000000002,
'matrix': array([[10. , 1. , 0.5],
[ 0. , 12. , 0.2],
[ 0. , 0. , 15. ]])}}
4. Coordinate transforms and wrapping¶
Two safe patterns when working with PBC: Convert to fractional coordinates, easy to reason about periodic images and Wrap positions into the primary cell before analysis/export.
import numpy as np
import molpy as mp
box = mp.Box.cubic(10.0)
points = np.array([
[12.0, -2.0, 5.0],
[25.0, 8.0, -3.0],
], dtype=float)
wrapped = box.wrap(points)
images = box.get_images(points)
unwrapped = box.unwrap(wrapped, images)
absolute = np.array([[5.0, 3.0, 7.0]], dtype=float)
fractional = box.make_fractional(absolute)
absolute_restored = box.make_absolute(fractional)
{
"points": points,
"wrapped": wrapped,
"images": images,
"unwrapped_matches_original": bool(np.allclose(unwrapped, points)),
"fractional": fractional,
"absolute_restored_matches": bool(np.allclose(absolute_restored, absolute)),
}
{'points': array([[12., -2., 5.],
[25., 8., -3.]]),
'wrapped': array([[2., 8., 5.],
[5., 8., 7.]]),
'images': array([[ 1, -1, 0],
[ 2, 0, -1]]),
'unwrapped_matches_original': True,
'fractional': array([[0.5, 0.3, 0.7]]),
'absolute_restored_matches': True}
5. Minimum-image distances¶
In a periodic system, the “closest” separation uses the minimum-image convention. Use:
diff, r1, r2 for the minimum-image displacement vector and dist, r1, r2 for the scalar distance.
import numpy as np
import molpy as mp
box = mp.Box.cubic(10.0)
r1 = np.array([[1.0, 1.0, 1.0]], dtype=float)
r2 = np.array([[9.5, 9.5, 9.5]], dtype=float)
dr = box.diff(r1, r2)
d = box.dist(r1, r2)
points1 = np.array([[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]], dtype=float)
points2 = np.array([[9.5, 9.5, 9.5], [8.0, 8.0, 8.0]], dtype=float)
distances = box.dist_all(points1, points2)
{
"dr": dr,
"distance": d,
"dist_all_shape": distances.shape,
"dist_all": distances,
}
{'dr': array([[1.5, 1.5, 1.5]]),
'distance': array([2.59807621]),
'dist_all_shape': (2, 2),
'dist_all': array([[2.59807621, 5.19615242],
[4.33012702, 6.92820323]])}
Summary¶
A Box is the cell geometry + PBC definition used for safe coordinate work., Construct common boxes with cubic / orth / tric, or provide a lattice matrix., Use wrap + fractional coordinates to keep periodic systems consistent., and Use diff / dist / dist_all for minimum-image distances..