diff --git a/gmso/core/atom.py b/gmso/core/atom.py index 8ee9993f1..fec601d8b 100644 --- a/gmso/core/atom.py +++ b/gmso/core/atom.py @@ -67,6 +67,7 @@ class Atom(Site): Refer to https://manual.gromacs.org/current/reference-manual/topologies/topology-file-formats.html for more information. """, + alias="restraint", ) model_config = ConfigDict( diff --git a/gmso/core/atom_type.py b/gmso/core/atom_type.py index 89f3ac99f..2ff55b2bb 100644 --- a/gmso/core/atom_type.py +++ b/gmso/core/atom_type.py @@ -14,6 +14,7 @@ unyt_compare, unyt_to_hashable, ) +from gmso.utils.units import GMSO_UnitRegistry class AtomType(ParametricPotential): @@ -254,7 +255,9 @@ def validate_charge(cls, charge): warnings.warn( UNIT_WARNING_STRING.format("Charges", "elementary charge") ) - charge *= u.elementary_charge + charge *= u.Unit( + "elementary_charge", registry=GMSO_UnitRegistry().reg + ) else: ensure_valid_dimensions(charge, u.elementary_charge) diff --git a/gmso/core/forcefield.py b/gmso/core/forcefield.py index f262de271..6275ccd63 100644 --- a/gmso/core/forcefield.py +++ b/gmso/core/forcefield.py @@ -56,7 +56,7 @@ class ForceField(object): If true, perform a strict validation of the forcefield XML file greedy: bool, default=True If True, when using strict mode, fail on the first error/mismatch - backend: str, default="gmso" + backend: str, default="forcefield-utilities" Can be "gmso" or "forcefield-utilities". This will define the methods to load the forcefield. @@ -579,6 +579,12 @@ def xml_from_forcefield_utilities(cls, filename): except: loader = FoyerFFs() ff = loader.load(filename).to_gmso_ff() + ff.units = { + "energy": "kJ", + "distance": "nm", + "mass": "amu", + "charge": "elementary_charge", + } return ff def to_xml(self, filename, overwrite=False, backend="gmso"): @@ -647,7 +653,7 @@ def _xml_from_gmso(self, filename, overwrite=False): "energy": "kJ", "distance": "nm", "mass": "amu", - "charge": "coulomb", + "charge": "elementary_charge", }, ) diff --git a/gmso/formats/json.py b/gmso/formats/json.py index 091f27d51..74effc64e 100644 --- a/gmso/formats/json.py +++ b/gmso/formats/json.py @@ -21,7 +21,7 @@ from gmso.formats.formats_registry import loads_as, saves_as -def _to_json(top, types=False, update=True): +def _to_json(top, types=True, update=False): """Return a json serializable dictionary from a topology. This method is used for json serializing the topology @@ -30,7 +30,7 @@ def _to_json(top, types=False, update=True): ---------- top: gmso.Topology, required The topology - types: bool, default=False + types: bool, default=True If true, include type info (i.e. Potentials) update: bool, default=False If true, update the topology before iterating through the files @@ -41,9 +41,10 @@ def _to_json(top, types=False, update=True): A json serializable dictionary representing members of this Topology """ if types and not top.is_typed(): - raise ValueError( + warnings.warn( "Cannot incorporate types because the topology is not typed." ) + types = False if not types and top.is_typed(): warnings.warn( @@ -282,7 +283,7 @@ def _from_json(json_dict): @saves_as(".json") -def write_json(top, filename, **kwargs): +def write_json(top, filename, types=True, update=False, **kwargs): """Save the topology as a JSON file. Parameters @@ -291,11 +292,17 @@ def write_json(top, filename, **kwargs): The topology to save filename: str, pathlib.Path The file to save to topology to, must be suffixed with .json + types: bool, default=True + If true, include type info (i.e. Potentials) + update: bool, default=False + If true, update the topology before iterating through the files **kwargs: dict The keyword arguments to _to_json and json.dump methods """ json_dict = _to_json( - top, update=kwargs.pop("update", True), types=kwargs.pop("types", False) + top, + update=update, + types=types, ) if not isinstance(filename, Path): filename = Path(filename).resolve() diff --git a/gmso/tests/test_serialization.py b/gmso/tests/test_serialization.py index fa508e872..6fbaeb588 100644 --- a/gmso/tests/test_serialization.py +++ b/gmso/tests/test_serialization.py @@ -224,7 +224,7 @@ def test_serialization_unsupported_file_format(self, ethane_from_scratch): ethane_from_scratch.save("ethane_from_scratch.zip") def test_serialization_untyped_with_types_info(self, ethane_from_scratch): - with pytest.raises(ValueError): + with pytest.warns(UserWarning): ethane_from_scratch.save("ethane_from_scratch.json", types=True) def test_serialization_overwrite(self, ethane_from_scratch): diff --git a/gmso/utils/units.py b/gmso/utils/units.py index fbd4f8032..e9468c5f2 100644 --- a/gmso/utils/units.py +++ b/gmso/utils/units.py @@ -82,22 +82,26 @@ def default_reg(): def register_general_units(reg: u.UnitRegistry): """Register units that are generally useful to a basic unyt UnitSystem.""" - conversion = 1 * getattr(u.physical_constants, "elementary_charge").value - dim = u.dimensions.current_mks * u.dimensions.time + elementary_charge_conversion = ( + 1 * getattr(u.physical_constants, "elementary_charge").value + ) + charge_dim = u.dimensions.current_mks * u.dimensions.time reg.add( "elementary_charge", - conversion, - dim, + elementary_charge_conversion, + charge_dim, r"\rm{e}", ) # proton charge - conversion = ( + + kb_conversion = ( 1 * getattr(u.physical_constants, "boltzmann_constant_mks").value ) - dim = u.dimensions.energy / u.dimensions.temperature + kb_dim = u.dimensions.energy / u.dimensions.temperature reg.add( - "kb", base_value=conversion, dimensions=dim, tex_repr=r"\rm{kb}" + "kb", base_value=kb_conversion, dimensions=kb_dim, tex_repr=r"\rm{kb}" ) # boltzmann temperature - conversion = ( + + bohr_rad_conversion = ( 4 * np.pi * getattr(u.physical_constants, "reduced_planck_constant").value ** 2 @@ -107,30 +111,35 @@ def register_general_units(reg: u.UnitRegistry): * getattr(u.physical_constants, "electron_mass").value ) ) - dim = u.dimensions.length + bohr_rad_dim = u.dimensions.length reg.add( - "a0", base_value=conversion, dimensions=dim, tex_repr=r"\rm{a0}" + "a0", + base_value=bohr_rad_conversion, + dimensions=bohr_rad_dim, + tex_repr=r"\rm{a0}", ) # bohr radius - conversion = ( + + hartree_conversion = ( getattr(u.physical_constants, "reduced_planck_constant").value ** 2 / u.Unit("a0", registry=reg).base_value ** 2 / getattr(u.physical_constants, "electron_mass").value ) - dim = u.dimensions.energy + hartree_dim = u.dimensions.energy reg.add( "Ehartree", - base_value=conversion, - dimensions=dim, + base_value=hartree_conversion, + dimensions=hartree_dim, tex_repr=r"\rm{Ehartree}", ) # Hartree energy - conversion = np.sqrt( + + static_coulomb_conversion = np.sqrt( 10**9 / (4 * np.pi * getattr(u.physical_constants, "eps_0").value) ) - dim = u.dimensions.charge + charge_dim = u.dimensions.charge reg.add( "Statcoulomb_charge", - base_value=conversion, - dimensions=dim, + base_value=static_coulomb_conversion, + dimensions=charge_dim, tex_repr=r"\rm{Statcoulomb_charge}", ) # Static charge