Skip to content

Commit

Permalink
Write charge in elementary charge unit (XML writer) (#794)
Browse files Browse the repository at this point in the history
* make default charge to be written out elementary_charge instead of coulomb

* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

* change default behavior for json writer

* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

* add missing alias for atom restraint

* change the types check in json writer to be a awarning

* fix unit test

* Update gmso/core/atom_type.py

Co-authored-by: CalCraven <54594941+CalCraven@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: CalCraven <54594941+CalCraven@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 16, 2024
1 parent c99799a commit 6c7fbcd
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 27 deletions.
1 change: 1 addition & 0 deletions gmso/core/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
5 changes: 4 additions & 1 deletion gmso/core/atom_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
unyt_compare,
unyt_to_hashable,
)
from gmso.utils.units import GMSO_UnitRegistry


class AtomType(ParametricPotential):
Expand Down Expand Up @@ -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)

Expand Down
10 changes: 8 additions & 2 deletions gmso/core/forcefield.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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"):
Expand Down Expand Up @@ -647,7 +653,7 @@ def _xml_from_gmso(self, filename, overwrite=False):
"energy": "kJ",
"distance": "nm",
"mass": "amu",
"charge": "coulomb",
"charge": "elementary_charge",
},
)

Expand Down
17 changes: 12 additions & 5 deletions gmso/formats/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion gmso/tests/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
45 changes: 27 additions & 18 deletions gmso/utils/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down

0 comments on commit 6c7fbcd

Please sign in to comment.