Skip to content

Commit

Permalink
Merge branch 'main' into pandas-simplification-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisjonesBSU authored Aug 19, 2024
2 parents b364e48 + cb19fd5 commit 72037d5
Show file tree
Hide file tree
Showing 25 changed files with 385 additions and 63 deletions.
45 changes: 38 additions & 7 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ on:
jobs:
test:
if: github.event.pull_request.draft == false
name: GMSO Tests
name: GMSO Tests (python)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macOS-latest, macOS-13, ubuntu-latest]
os: [ubuntu-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]

defaults:
run:
shell: bash -l {0}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Checkout Branch / Pull Request

- name: Install Mamba
Expand All @@ -43,11 +43,42 @@ jobs:
run: python -m pytest -v --cov=gmso --cov-report=xml --cov-append --cov-config=setup.cfg --color yes --pyargs gmso

- name: Upload Coverage Report
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v4
with:
name: GMSO-Coverage
verbose: true

arch-test:
if: github.event.pull_request.draft == false
name: GMSO Tests (arch)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macOS-latest, macOS-13, ubuntu-latest]
python-version: ["3.12"]

defaults:
run:
shell: bash -l {0}

steps:
- uses: actions/checkout@v4
name: Checkout Branch / Pull Request

- name: Install Mamba
uses: mamba-org/setup-micromamba@v1
with:
environment-file: environment-dev.yml
create-args: >-
python=${{ matrix.python-version }}
- name: Install Package
run: python -m pip install -e .

- name: Test (OS -> ${{ matrix.os }} / Python -> ${{ matrix.python-version }})
run: python -m pytest -v --color yes --pyargs gmso

bleeding-edge-test:
if: github.event.pull_request.draft == false
name: Bleeding Edge mosdef Tests for GMSO
Expand All @@ -57,14 +88,14 @@ jobs:
shell: bash -l {0}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Checkout Branch / Pull Request

- name: Install Mamba
uses: mamba-org/setup-micromamba@v1
with:
environment-file: environment-dev.yml
create-args: python=3.10
create-args: python=3.12

- name: Clone mBuild and Foyer and forcefield-utilities
run: |
Expand Down Expand Up @@ -120,7 +151,7 @@ jobs:
echo Docker Image tags: ${DOCKER_TAGS}
- name: Build and Push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
push: true
tags: ${{ env.DOCKER_TAGS }}
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.5
rev: v0.5.7
hooks:
# Run the linter.
- id: ruff
Expand Down
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@

project = "gmso"
copyright = "2024, mosdef-hub, Vanderbilt University"
author = "Matt Thompson, Alex Yang, Ray Matsumoto, Parashara Shamaprasad, Umesh Timalsina, Co D. Quach, Ryan S. DeFever, Justin Gilmer"
author = "Matt Thompson, Alex Yang, Ray Matsumoto, Parashara Shamaprasad, Umesh Timalsina, Co D. Quach, Ryan S. DeFever, Justin Gilmer, Nicholas C. Craven, Christopher R. Iacovella, Brad Crawford, and Chris Jones"

# The full version, including alpha/beta/rc tags
version = "0.12.1"
release = "0.12.1"
version = "0.12.4"
release = "0.12.4"


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion gmso/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
from .core.pairpotential_type import PairPotentialType
from .core.topology import Topology

__version__ = "0.12.1"
__version__ = "0.12.4"
147 changes: 139 additions & 8 deletions gmso/abc/abstract_site.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
"""Basic interaction site in GMSO that all other sites will derive from."""

import warnings
from typing import Any, ClassVar, NamedTuple, Optional, Sequence, TypeVar, Union
from typing import Any, ClassVar, Optional, Sequence, TypeVar, Union

import numpy as np
import unyt as u
from pydantic import (
ConfigDict,
Field,
StrictInt,
StrictStr,
field_serializer,
field_validator,
Expand All @@ -20,8 +19,124 @@
from gmso.exceptions import GMSOError

PositionType = Union[Sequence[float], np.ndarray, u.unyt_array]
MoleculeType = NamedTuple("Molecule", name=StrictStr, number=StrictInt)
ResidueType = NamedTuple("Residue", name=StrictStr, number=StrictInt)


class Molecule(GMSOBase):
def __repr__(self):
return (
f"Molecule(name={self.name}, residue={self.residue}, isrigid={self.isrigid}"
)

__iterable_attributes__: ClassVar[set] = {
"name",
"number",
"isrigid",
}

__base_doc__: ClassVar[str] = "Molecule label for interaction sites."

name_: str = Field(
"",
validate_default=True,
description="Name of the molecule",
alias="name",
)
number_: int = Field(
0,
description="The index/number of the molecule",
alias="number",
)
isrigid_: bool = Field(
False,
description="Indicate whether the molecule is rigid",
)
model_config = ConfigDict(
alias_to_fields={
"name": "name_",
"number": "number_",
"isrigid": "isrigid_",
}
)

@property
def name(self) -> str:
"""Return the name of the molecule."""
return self.__dict__.get("name_")

@property
def number(self) -> int:
"""Return the index/number of the moleucle."""
return self.__dict__.get("number_")

@property
def isrigid(self) -> bool:
"""Return the rigid label of the molecule."""
return self.__dict__.get("isrigid_")

def __hash__(self):
return hash(tuple([(name, val) for name, val in self.__dict__.items()]))

def __eq__(self, other):
"""Test if two objects are equivalent."""
if isinstance(other, (list, tuple)):
return all(
[val1 == val2 for val1, val2 in zip(self.__dict__.values(), other)]
)
else:
return self.__dict__ == other.__dict__


class Residue(GMSOBase):
def __repr__(self):
return f"Residue(name={self.name}, residue={self.residue}"

__iterable_attributes__: ClassVar[set] = {
"name",
"number",
}

__base_doc__: ClassVar[str] = "Residue label for interaction sites."

name_: str = Field(
"",
validate_default=True,
description="Name of the residue",
alias="name",
)
number_: int = Field(
0,
description="The index/number of the residue",
alias="number",
)
model_config = ConfigDict(
alias_to_fields={
"name": "name_",
"number": "number_",
}
)

@property
def name(self) -> str:
"""Return the name of the residue."""
return self.__dict__.get("name_")

@property
def number(self) -> int:
"""Return the index/number of the residue."""
return self.__dict__.get("number_")

def __hash__(self):
return hash(tuple([(name, val) for name, val in self.__dict__.items()]))

def __eq__(self, other):
"""Test if two objects are equivalent."""
if isinstance(other, (list, tuple)):
return all(
[val1 == val2 for val1, val2 in zip(self.__dict__.values(), other)]
)
else:
return self.__dict__ == other.__dict__


SiteT = TypeVar("SiteT", bound="Site")

Expand Down Expand Up @@ -76,13 +191,13 @@ class Site(GMSOBase):
alias="group",
)

molecule_: Optional[MoleculeType] = Field(
molecule_: Optional[Union[Molecule, list, tuple]] = Field(
None,
description="Molecule label for the site, format of (molecule_name, molecule_number)",
alias="molecule",
)

residue_: Optional[ResidueType] = Field(
residue_: Optional[Union[Residue, list, tuple]] = Field(
None,
description="Residue label for the site, format of (residue_name, residue_number)",
alias="residue",
Expand Down Expand Up @@ -126,7 +241,7 @@ def group(self) -> str:
return self.__dict__.get("group_")

@property
def molecule(self) -> tuple:
def molecule(self):
"""Return the molecule of the site."""
return self.__dict__.get("molecule_")

Expand Down Expand Up @@ -185,12 +300,28 @@ def is_valid_position(cls, position):
return position

@field_validator("name_")
def inject_name(cls, value):
def parse_name(cls, value):
if value == "" or value is None:
return cls.__name__
else:
return value

@field_validator("residue_")
def parse_residue(cls, value):
if isinstance(value, (tuple, list)):
assert len(value) == 2
value = Residue(name=value[0], number=value[1])
return value

@field_validator("molecule_")
def parse_molecule(cls, value):
if isinstance(value, (tuple, list)):
if len(value) == 2:
value = Molecule(name=value[0], number=value[1])
elif len(value) == 3:
value = Molecule(name=value[0], number=value[1], isrigid=value[2])
return value

@classmethod
def __new__(cls, *args: Any, **kwargs: Any) -> SiteT:
if cls is Site:
Expand Down
10 changes: 9 additions & 1 deletion gmso/core/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,22 @@ def element_by_smarts_string(smarts_string, verbose=False):
GMSOError
If no matching element is found for the provided smarts string
"""
from lark import UnexpectedCharacters

from gmso.utils.io import import_

foyer = import_("foyer")
SMARTS = foyer.smarts.SMARTS

PARSER = SMARTS()

symbols = PARSER.parse(smarts_string).iter_subtrees_topdown()
try:
symbols = PARSER.parse(smarts_string).iter_subtrees_topdown()
except UnexpectedCharacters:
raise GMSOError(
f"Failed to find an element from SMARTS string {smarts_string}. "
f"The SMARTS string contained unexpected characters."
)

first_symbol = None
for symbol in symbols:
Expand Down
Loading

0 comments on commit 72037d5

Please sign in to comment.