Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 3 additions & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[bumpversion]
current_version = 0.11.0
current_version = 0.12.0
commit = True
tag = True

[bumpversion:file:mudslide/version.py]

[bumpversion:file:docs/source/conf.py]
28 changes: 23 additions & 5 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,44 @@ on:
branches: [ master ]

jobs:
build:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Lint with pylint
run: |
pylint mudslide/ --fail-under=9.5
- name: Type check with mypy
run: |
mypy mudslide/

test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pytest
python -m pip install pytest pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
python -m pip install -e .
- name: Test with pytest
run: |
pytest
pytest --cov=mudslide --cov-report=term-missing
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
_build
_static
_templates
dist/
test/checks
.claude
.vscode
mudslide.report
35 changes: 29 additions & 6 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ recursive=no
# source root.
source-roots=

# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# suggestion-mode was removed in pylint 4.x

# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
Expand Down Expand Up @@ -200,11 +198,34 @@ good-names=i,
v,
V,
p,
P
P,
A,
B,
C,
D,
F,
G,
H,
L,
M,
N,
Q,
R,
T,
U,
W

# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Scientific/mathematical naming conventions:
# ^[A-Z][a-z0-9]+$ - PascalWord: Hbar, Trace, Reff, Peff, Sij, V12
# ^[A-Z]+[0-9]+$ - UPPER+digits: W00, H0, TV00, E1
# ^[A-Z]{2}$ - two-letter uppercase: HI, AA
# ^[A-Z]+_\w+$ - UPPER_word: NAC_matrix, M_inv, V_nuc, T_window
# ^[a-z_]+[A-Z]\w*$ - mixed_Case: dV, delR, ddP, kbT2, compute_delF, avg_KE
# ^[A-Z]\w*_$ - trailing underscore: ElectronicModel_, DiabaticModel_
# ^[A-Z][a-z]+[A-Z]\w*$ - PascalCamel: LmR, LpR
good-names-rgxs=^[A-Z][a-z0-9]+$,^[A-Z]+[0-9]+$,^[A-Z]{2}$,^[A-Z]+_\w+$,^[a-z_]+[A-Z]\w*$,^[A-Z]\w*_$,^[A-Z][a-z]+[A-Z]\w*$

# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
Expand Down Expand Up @@ -440,7 +461,9 @@ disable=raw-checker-failed,
deprecated-pragma,
use-symbolic-message-instead,
use-implicit-booleaness-not-comparison-to-string,
use-implicit-booleaness-not-comparison-to-zero
use-implicit-booleaness-not-comparison-to-zero,
too-many-positional-arguments,
too-few-public-methods

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down
53 changes: 29 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
# Fewest Switches Surface Hopping [![Build Status](https://github.com/smparker/mudslide/actions/workflows/python-package.yml/badge.svg)](https://github.com/smparker/mudslide/actions/workflows/python-package.yml) [![Documentation Status](https://readthedocs.org/projects/mudslide/badge/?version=latest)](https://mudslide.readthedocs.io/en/latest/?badge=latest)
Python implementation of Tully's Fewest Switches Surface Hopping (FSSH) for model problems including
a propagator and an implementation of Tully's model problems described in Tully, J.C. _J. Chem. Phys._ (1990) **93** 1061.
The current implementation works for diabatic as well as ab initio models, with two or more electronic states, and with one or more
dimensional potentials.
# Mudslide [![Build Status](https://github.com/smparker/mudslide/actions/workflows/python-package.yml/badge.svg)](https://github.com/smparker/mudslide/actions/workflows/python-package.yml) [![Documentation Status](https://readthedocs.org/projects/mudslide/badge/?version=latest)](https://mudslide.readthedocs.io/en/latest/?badge=latest)
A Python library for nonadiabatic molecular dynamics, implementing Fewest Switches Surface Hopping (FSSH)
and related methods. Includes Tully's model problems (Tully, J.C. _J. Chem. Phys._ (1990) **93** 1061)
as well as interfaces to ab initio electronic structure codes. Supports diabatic and adiabatic models
with two or more electronic states and one or more dimensional potentials.

## Contents
* `mudslide` package that contains
- implementation of all surface hopping methods
- nonadiabatic and adiabatic dynamics methods
- `SurfaceHoppingMD` - Standard FSSH implementation
- `Ehrenfest` - Ehrenfest dynamics
- `AugmentedFSSH` - Augmented FSSH implementation
- `AugmentedFSSH` - Augmented FSSH (A-FSSH) implementation
- `EvenSamplingTrajectory` - FSSH with even sampling of phase space
- collection of 1D models
- `AdiabaticMD` - Adiabatic (ground state) molecular dynamics
- collection of 1D model potentials
- `TullySimpleAvoidedCrossing`
- `TullyDualAvoidedCrossing`
- `TullyExtendedCouplingReflection`
- `SuperExchange`
- `SubotnikModelX`
- `SubotnikModelS`
- `SubotnikModelW`
- `SubotnikModelZ`
- `ShinMetiu`
- some 2D models
- `LinearVibronic`
- 2D models
- `Subotnik2D`
- ab initio interfaces
- `TMModel` - Turbomole interface for TDDFT-based NAMD
- `OpenMM` - OpenMM interface for classical MD
- `QMMM` - QM/MM combining Turbomole and OpenMM
- `HarmonicModel` - Harmonic approximation from Hessian data
* `mudslide` script that runs simple model trajectories
* `mudslide-surface` script that prints 1D surface and couplings
* `mud` script providing a unified CLI with subcommands
* `mudslide-surface` script that prints 1D surfaces and couplings

## Requirements
* numpy
Expand Down Expand Up @@ -85,10 +95,9 @@ will run 4 scattering simulations with a particle starting in the ground state (
* `total_time` - total simulation length (default: 2 * abs(position/velocity))
* `samples` - number of trajectories to run (default: 2000)
* `seed` - random seed for trajectories (defaults however numpy does)
* `propagator` - method used to propagate electronic wavefunction
* "exponential" (default) - apply exponentiated Hamiltonian via diagonalization
* "ode" - scipy's ODE integrator
* `nprocs` - number of processes over which to parallelize trajectories (default: 1)
* `electronic_integration` - method used to propagate electronic wavefunction
* "exp" (default) - apply exponentiated Hamiltonian via diagonalization
* "linear-rk4" - interpolated RK4 integration
* `outcome_type` - how to count statistics at the end of a trajectory
* "state" (default) - use the state attribute of the simulation only
* "populations" - use the diagonals of the density matrix
Expand All @@ -105,15 +114,17 @@ and should implement
### compute() function
The `compute()` function needs to have the following signature:

def compute(self, X: ArrayLike, couplings: Any = None, gradients: Any None, reference: Any = None) -> None
def compute(self, X: ArrayLike, couplings: Any = None, gradients: Any = None, reference: Any = None) -> None

The `X` input to the `compute()` function is an array of the positions. All other inputs are ignored for now
but will eventually be used to allow the trajectory to enumerate precisely which quantities are desired at
each call.
At the end of the `compute()` function, the object must store
* `self.hamiltonian` - An `nstates x nstates` array of the Hamiltonian (`nstates` is the number of electronic states)
* `self.force` - An `nstates x ndim` array of the force on each PES (`ndim` is the number of classical degrees of freedom)
* `self.derivative_coupling` - An `nstates x nstates x ndim` array where `self.derivative_coupling[i,j,:]` contains <i|d/dR|j>.
* `self._hamiltonian` - An `nstates x nstates` array of the Hamiltonian (`nstates` is the number of electronic states)
* `self._force` - An `nstates x ndim` array of the force on each PES (`ndim` is the number of classical degrees of freedom)
* `self._derivative_coupling` - An `nstates x nstates x ndim` array where `derivative_coupling[i,j,:]` contains <i|d/dR|j>
* `self._forces_available` - A boolean array of length `nstates` indicating which forces were computed
* `self._derivative_couplings_available` - An `nstates x nstates` boolean array indicating which couplings were computed

See the file `mudslide/turbomole_model.py` for an example of a standalone ab initio model.

Expand Down Expand Up @@ -143,12 +154,6 @@ For batch runs, one must tell `BatchedTraj` how to decide on new initial conditi
and how to decide when a trajectory has finished. The basic requirements for each of those
is simple.

The structure of these classes is somewhat strange because of the limitations of
multiprocessing in python. To make use of multiprocessing, every object
must be able to be `pickle`d, meaning that multiprocessing inherits all the
same limitations. As a result, when using multiprocessing, the trajectory generator class must
be fully defined in the default namespace.

### Generating initial conditions
This should be a generator function that accepts a number of samples
and returns a dictionary with starting conditions filled in, e.g.,
Expand Down
5 changes: 3 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
author = 'Shane M. Parker'

# The full version, including alpha/beta/rc tags
version = '0.11.0'
release = '0.11.0'
from mudslide.version import __version__
version = __version__
release = __version__

master_doc = 'index'

Expand Down
110 changes: 110 additions & 0 deletions docs/source/developing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
Developer Guide
====================================

This page describes how to set up a development environment
and run the checks that are required to pass CI.

Setting Up a Development Environment
-------------------------------------
Clone the repository and install in editable mode with the
development dependencies:

.. code-block:: shell

$ git clone https://github.com/smparker/mudslide.git
$ cd mudslide
$ pip install -e ".[dev]"

This installs the package along with ``pytest``, ``mypy``, ``pylint``,
and ``yapf``.

Running the CI Checks Locally
------------------------------
The GitHub Actions CI runs three checks on every push and pull request:
tests, linting, and type checking. You should run all three locally
before pushing to make sure CI will pass.

Tests (pytest)
^^^^^^^^^^^^^^
Run the full test suite with:

.. code-block:: shell

$ pytest

All tests must pass. To run a single test file:

.. code-block:: shell

$ pytest test/test_math.py

Linting (pylint)
^^^^^^^^^^^^^^^^^
Run pylint with:

.. code-block:: shell

$ pylint mudslide/

CI requires a minimum score of **9.5/10**. The project's ``.pylintrc``
file configures allowed variable names, disabled checks, and other
settings. If pylint reports a score below 9.5, you can see individual
messages to understand what needs to be fixed.

You can also check a specific file:

.. code-block:: shell

$ pylint mudslide/batch.py

Type Checking (mypy)
^^^^^^^^^^^^^^^^^^^^^
Run mypy with:

.. code-block:: shell

$ mypy mudslide/

This must exit with **zero errors**. The mypy configuration in
``pyproject.toml`` enforces strict settings including
``disallow_untyped_defs`` and ``disallow_incomplete_defs``, so all
functions must have type annotations.

If you are adding new code, make sure to include type annotations on
all function signatures. Common patterns in the codebase:

.. code-block:: python

import numpy as np
from numpy.typing import ArrayLike, NDArray

def compute_energy(x: ArrayLike) -> NDArray:
...

Code Formatting (yapf)
^^^^^^^^^^^^^^^^^^^^^^^
The project uses ``yapf`` with a custom style. To format
a file in place:

.. code-block:: shell

$ yapf -i mudslide/batch.py

To format all source files:

.. code-block:: shell

$ yapf -i mudslide/*.py

Formatting is not currently enforced in CI, but consistent formatting
is expected for contributions.

Running All Checks
^^^^^^^^^^^^^^^^^^^
To run all three CI checks in sequence:

.. code-block:: shell

$ pytest && pylint mudslide/ --fail-under=9.5 && mypy mudslide/

If all three commands succeed, your changes should pass CI.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Mudslide!
components
usage
library
developing
references

Indices and tables
Expand Down
7 changes: 6 additions & 1 deletion mudslide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

from .version import __version__
from .header import print_header
from .exceptions import (
MudslideError, ConfigurationError, ExternalCodeError,
ConvergenceError, ComputeError, MissingDataError,
MissingForceError, MissingCouplingError
)

from . import units
from . import models
Expand All @@ -18,6 +23,7 @@
from . import surface
from . import turbo_make_harmonic

from .trajectory_md import *
from .surface_hopping_md import *
from .adiabatic_propagator import *
from .adiabatic_md import *
Expand All @@ -26,4 +32,3 @@
from .afssh import *
from .batch import *
from .tracer import *

Loading