Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap box with molbox's API #565

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies:
- ipywidgets
- ele>=0.2.0
- pre-commit
- molbox
- pandas
- symengine
- python-symengine
Expand Down
3 changes: 2 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ dependencies:
- lxml
- pydantic>=2
- networkx
- ele>=0.2.0
- ele >= 0.2.0
- molbox
- foyer>=0.11.3
- forcefield-utilities>=0.2.1
- symengine
Expand Down
68 changes: 57 additions & 11 deletions gmso/core/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import numpy as np
import unyt as u
from molbox import Box as MolBox
from unyt.array import allclose_units


Expand All @@ -22,6 +23,7 @@ def _validate_lengths(lengths):
np.reshape(lengths, newshape=(3,), order="C")

lengths *= input_unit

if input_unit != u.Unit("dimensionless"):
lengths.convert_to_units(u.nm)

Expand Down Expand Up @@ -119,30 +121,74 @@ class Box(object):

"""

def __init__(self, lengths, angles=None):
def __init__(self, lengths, angles=None, precision=None):
"""Construct a `Box` based on lengths and angles."""
self._lengths = _validate_lengths(lengths)
self._angles = _validate_angles(angles)
lengths = _validate_lengths(lengths)
angles = _validate_angles(angles)
print(lengths, angles)
precision = int(precision or 6)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this set precision to 6 if you cant typecast precision to an int?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it does because you have handled that in Molbox.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah true, that makes sense.

self._box = MolBox(
lengths=lengths.value, angles=angles.value, precision=precision
)

@property
def lengths(self):
"""Return edge lengths of the box."""
return self._lengths
return self._box.lengths * u.nm

@property
def angles(self):
"""Return angles of the box."""
return self._angles
return self._box.angles * u.degree

@lengths.setter
def lengths(self, lengths):
"""Set the lengths of the box."""
self._lengths = _validate_lengths(lengths)
lengths = _validate_lengths(lengths).value
self._set_internal_box_vectors(lengths=lengths)

@angles.setter
def angles(self, angles):
"""Set the angles of the box."""
self._angles = _validate_angles(angles)
angles = _validate_angles(angles).value
self._set_internal_box_vectors(angles=angles)

@property
def precision(self):
"""Amount of decimals to represent floating point values."""
return self._box._precision

@precision.setter
def precision(self, precision):
"""Decimal point precision(default=16)."""
self._box.precision = precision

def _set_internal_box_vectors(self, lengths=None, angles=None):
from molbox.box import _lengths_angles_to_vectors

if angles is None:
angles = self.angles.value
if lengths is None:
lengths = self.lengths.value

self._box._vectors = _lengths_angles_to_vectors(
lengths, angles, self._box.precision
)

(
Lx,
Ly,
Lz,
xy,
xz,
yz,
) = self._box._from_vecs_to_lengths_tilt_factors()
self._box._Lx = Lx
self._box._Ly = Ly
self._box._Lz = Lz
self._box._xy = xy
self._box._xz = xz
self._box._yz = yz

def _unit_vectors_from_angles(self):
"""Return unit vectors describing prism from angles."""
Expand Down Expand Up @@ -175,7 +221,7 @@ def _unit_vectors_from_angles(self):

def get_vectors(self):
"""Return the vectors of the box."""
return (self._lengths * self.get_unit_vectors().T).T
return (self.lengths * self.get_unit_vectors().T).T

def get_unit_vectors(self):
"""Return the normalized vectors of the box."""
Expand All @@ -185,14 +231,14 @@ def json_dict(self):
from gmso.abc.serialization_utils import unyt_to_dict

return {
"lengths": unyt_to_dict(self._lengths),
"angles": unyt_to_dict(self._angles),
"lengths": unyt_to_dict(self.lengths),
"angles": unyt_to_dict(self.angles),
}

def __repr__(self):
"""Return formatted representation of the box."""
return "Box(a={}, b={}, c={}, alpha={}, beta={}, gamma={})".format(
*self._lengths, *self._angles
*self.lengths, *self.angles
)

def __eq__(self, other):
Expand Down
1 change: 1 addition & 0 deletions gmso/formats/lammpsdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def write_lammpsdata(
unit_style
)
)

if unit_style != "lj" and lj_cfactorsDict:
raise ValueError(
"lj_cfactorsDict argument is only used if unit_style is lj."
Expand Down
6 changes: 4 additions & 2 deletions gmso/tests/test_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import pytest
import unyt as u
from molbox.box import BoxError
from unyt.testing import assert_allclose_units

from gmso.core.box import Box
Expand All @@ -25,7 +26,7 @@ def test_bad_lengths(self, lengths, angles):
Box(lengths=lengths, angles=angles)

def test_build_2D_Box(self):
with pytest.warns(UserWarning):
with pytest.raises(BoxError):
Box(lengths=u.nm * [4, 4, 0])

def test_dtype(self, box):
Expand All @@ -47,7 +48,8 @@ def test_angles_setter(self, lengths, angles):
box = Box(lengths=lengths, angles=u.degree * np.ones(3))
angles *= u.degree
box.angles = angles
assert (box.angles == angles).all()
print(angles, box.angles)
assert u.allclose_units(angles, box.angles, rtol=10 ** (-box.precision))

@pytest.mark.parametrize("lengths", [[3, 3, 3], [4, 4, 4], [4, 6, 4]])
def test_setters_with_lists(self, lengths):
Expand Down
10 changes: 2 additions & 8 deletions gmso/tests/test_lammps.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,10 @@ def test_read_lammps_triclinic(self, typed_ar_system):

read = gmso.Topology.load("triclinic.lammps")
assert_allclose_units(
read.box.lengths,
u.unyt_array([1, 1, 1], u.nm),
rtol=1e-5,
atol=1e-8,
read.box.lengths, u.unyt_array([1.0, 1.0, 1.0], u.nm)
)
assert_allclose_units(
read.box.angles,
u.unyt_array([60, 90, 120], u.degree),
rtol=1e-5,
atol=1e-8,
read.box.angles, u.unyt_array([60, 90, 120], u.degree), rtol=1e-5
)

def test_read_n_bonds(self, typed_ethane_opls):
Expand Down
Loading