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

Abstract_Site_Representations #523

Open
wants to merge 3 commits into
base: old_main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions gmso/core/bead.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import warnings
from typing import Optional, Union

import unyt as u

from pydantic import Field, validator

from gmso.core.element import Element
from gmso.abc.abstract_site import Site
from gmso.core.atom_type import AtomType
from gmso.utils.misc import ensure_valid_dimensions
from gmso.utils._constants import UNIT_WARNING_STRING


class Bead(Site):
__base_doc__ = """A bead represents a single coarse-grained association in a topology.

Beads are the representation of an object within `gmso` that describes any general
non-atomistic particle in gmso. Beads also contain information that are unique to
elements vs other types of interaction sites in molecular simulations.

Notes
-----
Beads have all the attributes inherited from the base Site class,
The order of precedence when attaining properties `charge` and `mass` is:

1. atom.charge > atom.atom_type.charge
2. atom.mass > atom.atom_type.mass

Examples
--------
>>> from gmso.core.bead import Bead
>>> bead1 = Bead(name='SP1')

See Also
--------
gmso.abc.AbstractSite
An Abstract Base class for implementing site objects in GMSO. The class Bead bases from
the gmso.abc.abstract site class
"""
charge_: Optional[Union[u.unyt_quantity, float]] = Field(
None,
description='Charge of the bead',
)

mass_: Optional[Union[u.unyt_quantity, float]] = Field(
None,
description='Mass of the bead'
)

bead_type_: Optional[AtomType] = Field(
None,
description='AtomType associated with the atom'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
description='AtomType associated with the atom'
description='AtomType associated with the bead'

should this be bead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good catch @jennyfothergill. I'm going to go through and try to write some more implementation for the bead class. Based on the properties you mentioned above, looks like it will act relatively similarly to the current site class.

)

@property
def charge(self) -> Union[u.unyt_quantity, None]:
charge = self.__dict__.get('charge_', None)
atom_type = self.__dict__.get('atom_type_', None)
if charge is not None:
return charge
elif bead_type is not None:
return bead_type.charge
else:
return None

@property
def mass(self) -> Union[u.unyt_quantity, None]:
mass = self.__dict__.get('mass_', None)
atom_type = self.__dict__.get('atom_type_', None)
if mass is not None:
return mass
elif bead_type is not None:
return bead_type.mass
else:
return None

@property
def bead_type(self) -> Union[AtomType, None]:
return self.__dict__.get('bead_type_', None)

def __le__(self, other):
if isinstance(other, Bead):
return hash(self) <= hash(other)
else:
raise TypeError(
f'Cannot compare equality between {type(self)} and {type(other)}'
)

def __lt__(self, other):
if isinstance(other, Bead):
return hash(self) < hash(other)
else:
raise TypeError(
f'Cannot compare equality between {type(self)} and {type(other)}'
)

@validator('charge_')
def is_valid_charge(cls, charge):
if charge is None:
return None
if not isinstance(charge, u.unyt_array):
warnings.warn(UNIT_WARNING_STRING.format('Charges', 'elementary charge'))
charge *= u.elementary_charge
else:
ensure_valid_dimensions(charge, u.elementary_charge)

return charge

@validator('mass_')
def is_valid_mass(cls, mass):
if mass is None:
return None
default_mass_units = u.gram /u.mol
if not isinstance(mass, u.unyt_array):
warnings.warn(UNIT_WARNING_STRING.format('Masses', 'g/mol'))
mass *= default_mass_units
else:
ensure_valid_dimensions(mass, default_mass_units)
return mass

class Config:
extra = 'forbid'

fields = {
'charge_': 'charge',
'mass_': 'mass',
'bead_type_': 'bead_type'
}

alias_to_fields = {
'charge': 'charge_',
'mass': 'mass_',
'bead_type': 'bead_type_'
}

validate_assignment = True