Skip to content

Commit 6183632

Browse files
Merge pull request #1261 from chrisjonesBSU/path-class
mBuild 2.0 polymer and simulation improvements: Introduce `Path` class, `Polymer.build_from_path()` and add new HOOMD-Blue simulation methods.
2 parents 626891b + fecd7b8 commit 6183632

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3876
-1356
lines changed

.github/workflows/CI.yaml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ on:
44
push:
55
branches:
66
- "main"
7+
- "develop"
78
pull_request:
89
branches:
910
- "main"
11+
- "develop"
1012
schedule:
1113
- cron: "0 0 * * *"
1214

@@ -19,7 +21,7 @@ jobs:
1921
fail-fast: false
2022
matrix:
2123
os: [ubuntu-latest]
22-
python-version: ["3.9", "3.10", "3.11", "3.12"]
24+
python-version: ["3.10", "3.11", "3.12"]
2325

2426
defaults:
2527
run:
@@ -39,6 +41,10 @@ jobs:
3941
- name: Install Package
4042
run: python -m pip install -e .
4143

44+
- name: Conditionally install OpenBabel
45+
if: ${{ matrix.python-version != '3.13' }}
46+
run: micromamba install -y openbabel
47+
4248
- name: Test (OS -> ${{ matrix.os }} / Python -> ${{ matrix.python-version }})
4349
run: python -m pytest -v --cov=mbuild --cov-report=xml --cov-append --cov-config=setup.cfg --color yes --pyargs mbuild
4450

@@ -57,7 +63,7 @@ jobs:
5763
fail-fast: false
5864
matrix:
5965
os: [macOS-latest, macOS-13, ubuntu-latest]
60-
python-version: ["3.12"]
66+
python-version: ["3.13"]
6167

6268
defaults:
6369
run:
@@ -89,7 +95,7 @@ jobs:
8995
- uses: actions/checkout@v4
9096
name: Checkout Branch / Pull Request
9197

92-
- uses: Vampire/setup-wsl@v4
98+
- uses: Vampire/setup-wsl@v6
9399
with:
94100
distribution: Ubuntu-24.04
95101
wsl-shell-user: runner

.github/workflows/codeql.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: "CodeQL"
22

33
on:
44
push:
5-
branches: [ "main" ]
5+
branches: [ "develop" ]
66
pull_request:
7-
branches: [ "main" ]
7+
branches: [ "develop" ]
88
schedule:
99
- cron: "50 6 * * 0"
1010

@@ -27,15 +27,15 @@ jobs:
2727
uses: actions/checkout@v3
2828

2929
- name: Initialize CodeQL
30-
uses: github/codeql-action/init@v2
30+
uses: github/codeql-action/init@v3
3131
with:
3232
languages: ${{ matrix.language }}
3333
queries: +security-and-quality
3434

3535
- name: Autobuild
36-
uses: github/codeql-action/autobuild@v2
36+
uses: github/codeql-action/autobuild@v3
3737

3838
- name: Perform CodeQL Analysis
39-
uses: github/codeql-action/analyze@v2
39+
uses: github/codeql-action/analyze@v3
4040
with:
4141
category: "/language:${{ matrix.language }}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ test-output.xml
33
*.pymon
44
*.ipynb_checkpoints
55
*DS_Store*
6+
__pycache__
67

78
# C extensions
89
*.so

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ ci:
1010
repos:
1111
- repo: https://github.com/astral-sh/ruff-pre-commit
1212
# Ruff version.
13-
rev: v0.11.13
13+
rev: v0.14.2
1414
hooks:
1515
# Run the linter.
1616
- id: ruff
1717
args: [--line-length=80, --fix]
1818
# Run the formatter.
1919
- id: ruff-format
2020
- repo: https://github.com/pre-commit/pre-commit-hooks
21-
rev: v5.0.0
21+
rev: v6.0.0
2222
hooks:
2323
- id: check-yaml
2424
- id: end-of-file-fixer
2525
- id: trailing-whitespace
2626

2727
- repo: https://github.com/pycqa/isort
28-
rev: 6.0.1
28+
rev: 7.0.0
2929
hooks:
3030
- id: isort
3131
name: isort (python)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ conda activate mbuild-dev
6161
pip install .
6262
```
6363

64+
NOTE: [openbabel](https://github.com/openbabel/openbabel) is required for some energy minimization methods in `mbuild.compound.Compound()`. It can be installed into your mBuild environment from conda-forge with `conda install -c conda-forge openbabel`; however, openbabel does not yet support python 3.13.
65+
6466
#### Install an editable version from source
6567

6668
Once all dependencies have been installed and the ``conda`` environment has been created, the ``mBuild`` itself can be installed.

codecov.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
ignore:
22
- "mbuild/examples"
33
- "mbuild/tests"
4+
- "mbuild/__init__.py"

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@
158158
# built documents.
159159
#
160160

161-
version = "1.2.0"
162-
release = "1.2.0"
161+
version = "1.3.0"
162+
release = "1.3.0"
163163

164164

165165
# The language for content autogenerated by Sphinx. Refer to documentation

environment-dev.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@ name: mbuild-dev
22
channels:
33
- conda-forge
44
dependencies:
5-
- python>=3.9,<=3.12
5+
- python>=3.10,<=3.13
66
- boltons
7-
- numpy=1.26.4
7+
- numba
8+
- numpy>=2.0,<2.3
89
- sympy
910
- unyt>=2.9.5
1011
- boltons
1112
- lark>=1.2
1213
- lxml
13-
- freud>=3.0
1414
- intermol
1515
- mdtraj
1616
- pydantic>=2
1717
- networkx
1818
- nglview>=3
1919
- pytest
2020
- garnett>=0.7.1
21-
- openbabel>=3.0.0
22-
- openff-toolkit-base >=0.11,<0.16.7
21+
- openff-toolkit-base>0.16.7
2322
- openmm
2423
- gsd>=2.9
24+
- freud>=3.2
2525
- parmed>=3.4.3
2626
- packmol>=20.15
2727
- pytest-cov
@@ -39,8 +39,8 @@ dependencies:
3939
- pandas
4040
- symengine
4141
- python-symengine
42-
- hoomd>=4.0,<5.0
4342
- py3Dmol
43+
- hoomd>=4.0
4444
- pip:
4545
- git+https://github.com/mosdef-hub/gmso.git@main
4646
- git+https://github.com/mosdef-hub/foyer.git@main

environment.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ channels:
33
- conda-forge
44
dependencies:
55
- ele
6-
- numpy=1.26.4
6+
- numpy>=2.0,<2.3
77
- packmol>=20.15
8-
- gmso>=0.9.0
8+
- gmso>=0.12.0
99
- garnett
10+
- numba
1011
- parmed>=3.4.3
1112
- pycifrw
12-
- python>=3.8
13+
- python>=3.10,<=3.13
1314
- rdkit>=2021
1415
- scipy
1516
- networkx

mbuild/__init__.py

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
# ruff: noqa: F403
33
"""mBuild: a hierarchical, component based molecule builder."""
44

5+
import logging
6+
import sys
7+
from logging.handlers import RotatingFileHandler
8+
59
from mbuild.box import Box
610
from mbuild.coarse_graining import coarse_grain
711
from mbuild.compound import *
@@ -10,8 +14,124 @@
1014
from mbuild.lattice import Lattice
1115
from mbuild.packing import *
1216
from mbuild.pattern import *
17+
from mbuild.polymer import Polymer
1318
from mbuild.port import Port
1419
from mbuild.recipes import recipes
1520

16-
__version__ = "1.2.0"
21+
__version__ = "1.3.0"
1722
__date__ = "2025-01-23"
23+
24+
25+
class DeduplicationFilter(logging.Filter):
26+
"""A logging filter that suppresses duplicate messages."""
27+
28+
def __init__(self):
29+
super().__init__()
30+
self.logged_messages = set()
31+
32+
def filter(self, record):
33+
log_entry = (record.name, record.levelno, record.msg)
34+
if log_entry not in self.logged_messages:
35+
self.logged_messages.add(log_entry)
36+
return True
37+
return False
38+
39+
40+
class HeaderRotatingFileHandler(RotatingFileHandler):
41+
def __init__(
42+
self,
43+
filename,
44+
mode="w",
45+
maxBytes=0,
46+
backupCount=0,
47+
encoding=None,
48+
delay=False,
49+
header="",
50+
):
51+
self.header = header
52+
super().__init__(filename, mode, maxBytes, backupCount, encoding, delay)
53+
54+
def _open(self):
55+
"""
56+
Open the current base log file, with the header written.
57+
"""
58+
stream = super()._open()
59+
if stream.tell() == 0 and self.header: # Only write header if file is empty
60+
stream.write(self.header + "\n")
61+
return stream
62+
63+
64+
class mBuildLogger:
65+
def __init__(self):
66+
self.library_logger = logging.getLogger("mbuild")
67+
self.library_logger.setLevel(logging.DEBUG)
68+
69+
# Create handlers
70+
self.console_handler = logging.StreamHandler(sys.stdout)
71+
self.console_handler.setLevel(logging.WARNING)
72+
73+
# Create a formatter
74+
self.formatter = logging.Formatter(
75+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
76+
)
77+
78+
# Add formatter to handlers
79+
self.console_handler.setFormatter(self.formatter)
80+
81+
# Initialize and add the deduplication filter
82+
self.dedup_filter = DeduplicationFilter()
83+
self.console_handler.addFilter(self.dedup_filter)
84+
85+
# Clear any previous handlers to avoid duplicates in Jupyter
86+
self._clear_handlers()
87+
88+
# Add handlers to the library logger
89+
self.library_logger.addHandler(self.console_handler)
90+
91+
def _clear_handlers(self):
92+
handlers = self.library_logger.handlers[:]
93+
for handler in handlers:
94+
self.library_logger.removeHandler(handler)
95+
96+
def debug_file(self, filename: str):
97+
"""Print logging Debug messages to file `filename`."""
98+
# Get the path to the Python interpreter
99+
python_executable = sys.executable
100+
101+
# Get the list of command-line arguments
102+
command_arguments = sys.argv
103+
104+
# Construct the full command
105+
full_command = [python_executable] + command_arguments
106+
header = f"Log details for mBuild {__version__} from running \n{full_command}"
107+
self.file_handler = HeaderRotatingFileHandler(
108+
filename, mode="a", maxBytes=10**6, backupCount=2, header=header
109+
)
110+
self.file_handler.setLevel(logging.DEBUG)
111+
112+
self.file_handler.addFilter(DeduplicationFilter()) # fresh duplication handler
113+
self.file_handler.setFormatter(self.formatter)
114+
self.library_logger.addHandler(self.file_handler)
115+
116+
def print_level(self, level: str):
117+
"""Print sys.stdout screen based on the logging `level` passed."""
118+
levelDict = {
119+
"notset": logging.NOTSET,
120+
"debug": logging.DEBUG,
121+
"info": logging.INFO,
122+
"warning": logging.WARNING,
123+
"error": logging.ERROR,
124+
"critical": logging.CRITICAL,
125+
}
126+
logLevel = levelDict.get(level.lower())
127+
if logLevel:
128+
self.console_handler.setLevel(logLevel) # sets stdout
129+
else:
130+
raise ValueError(
131+
f"INCORRECT {level=}. Please set level of {levelDict.keys()}"
132+
)
133+
134+
135+
# Example usage in __init__.py
136+
mbuild_logger = mBuildLogger()
137+
mbuild_logger.library_logger.setLevel(logging.INFO)

0 commit comments

Comments
 (0)