├── .git-blame-ignore-revs ├── .github ├── dependabot.yml └── workflows │ ├── pre-commit.yml │ └── pytest.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── LICENSE.md ├── README.md ├── create_conda_environment.sh ├── docs ├── Makefile ├── conf.py ├── dev │ ├── contributing.rst │ └── installation.rst ├── examples │ ├── example1.rst │ ├── example2.rst │ └── example3.rst ├── images │ └── AL_scheme.png ├── index.rst ├── make.bat ├── references │ ├── box.rst │ ├── configurations │ │ ├── calculate.rst │ │ ├── configuration.rst │ │ ├── configuration_set.rst │ │ ├── index.rst │ │ ├── plotting.rst │ │ └── trajectory.rst │ ├── descriptor │ │ ├── _base.rst │ │ ├── index.rst │ │ └── soap_descriptor.rst │ ├── energy.rst │ ├── forces.rst │ ├── index.rst │ ├── loss │ │ ├── _base.rst │ │ ├── index.rst │ │ ├── mean_errors.rst │ │ └── tau.rst │ ├── molecule.rst │ ├── potentials │ │ ├── _base.rst │ │ ├── ace.rst │ │ ├── gap.rst │ │ ├── index.rst │ │ └── mace.rst │ ├── sampling │ │ ├── _base.rst │ │ ├── bias.rst │ │ ├── index.rst │ │ ├── md.rst │ │ ├── md_openmm.rst │ │ ├── metadynamics.rst │ │ ├── plumed.rst │ │ ├── reaction_coord.rst │ │ └── umbrella.rst │ ├── system.rst │ ├── training │ │ ├── active.rst │ │ ├── index.rst │ │ └── selection.rst │ └── utils.rst └── tutorials │ ├── tutorial1.rst │ ├── tutorial2.rst │ └── tutorial3.rst ├── environment.yml ├── environment_ace.yml ├── environment_mace.yml ├── examples ├── DA_paper │ ├── 1d_fes │ │ ├── cis_endo_TS_wB97M.xyz │ │ ├── fes.py │ │ ├── h2o.xyz │ │ └── neb_optimised.xyz │ ├── 2D_pes │ │ ├── cis_endo_TS_water.xyz │ │ └── pes.py │ ├── readme.md │ ├── training.png │ ├── training │ │ ├── explicit │ │ │ ├── cis_endo_TS_wB97M.xyz │ │ │ ├── endo_ace_ex.py │ │ │ └── h2o.xyz │ │ └── implicit │ │ │ ├── cis_endo_TS_water.xyz │ │ │ └── endo_ace_im.py │ └── uphill │ │ ├── cis_endo_TS_wB97M.xyz │ │ ├── generate_rs.py │ │ ├── h2o.xyz │ │ └── uphill.py ├── WTMetaD_paper │ ├── README.md │ ├── r1 │ │ ├── al_downhill │ │ │ ├── r1_ts.xyz │ │ │ └── training.py │ │ └── al_wtmetad │ │ │ ├── ch3cl_f.xyz │ │ │ └── training.py │ ├── r2 │ │ ├── al_downhill │ │ │ ├── al_downhill.py │ │ │ └── ts_r2.xyz │ │ ├── al_wtmetad │ │ │ ├── al_wtmetad.py │ │ │ └── r_r2.xyz │ │ ├── free_energy │ │ │ ├── umbrella │ │ │ │ ├── r2_irc.xyz │ │ │ │ ├── ts_r2.xyz │ │ │ │ └── umbrella.py │ │ │ ├── wtmetad │ │ │ │ ├── r_r2.xyz │ │ │ │ └── wtmetad.py │ │ │ └── wtmetad_ib │ │ │ │ ├── accumulated_bias │ │ │ │ └── bias_after_iter_15.dat │ │ │ │ ├── r_r2.xyz │ │ │ │ └── wtmetad_ib.py │ │ └── structure │ │ │ ├── p_r2.xyz │ │ │ ├── r_r2.xyz │ │ │ └── ts_r2.xyz │ └── r3 │ │ ├── CCl2.xyz │ │ ├── gly_gas.xyz │ │ └── training.py ├── da_ts.py ├── da_ts.xyz ├── inherited_bias_active_learning │ ├── h2 │ │ ├── h2.py │ │ └── h2.xyz │ └── h2o │ │ ├── h2o.py │ │ └── h2o.xyz ├── metadynamics │ ├── h2 │ │ ├── h2.py │ │ └── h2.xyz │ └── h2o │ │ ├── h2o.py │ │ └── h2o.xyz ├── methane.py ├── methane.xyz ├── paper_examples │ ├── r1_fig2 │ │ ├── ace_parity │ │ │ ├── da_data.npz │ │ │ ├── train.py │ │ │ └── ts_pbe0.xyz │ │ └── mlp_comparison │ │ │ ├── ace │ │ │ ├── train.py │ │ │ └── ts_pbe0.xyz │ │ │ ├── ace_uplift │ │ │ ├── train.py │ │ │ ├── ts_pbe0.xyz │ │ │ └── uplift │ │ │ │ ├── train.py │ │ │ │ └── ts_pbe0.xyz │ │ │ ├── gap │ │ │ ├── train.py │ │ │ └── ts_pbe0.xyz │ │ │ └── nequip │ │ │ ├── train.py │ │ │ └── ts_pbe0.xyz │ ├── r2_fig3 │ │ ├── train_plus_ts7.py │ │ ├── train_ts5.py │ │ ├── ts5.xyz │ │ └── ts7.xyz │ ├── r3_fig4 │ │ ├── cis_endo_TS_M06.xyz │ │ ├── cis_endo_TS_PBE0.xyz │ │ ├── cis_endo_TS_wB97X.xyz │ │ ├── endo_ace_M06.py │ │ ├── endo_ace_PBE0.py │ │ └── endo_ace_wB97X.py │ ├── r3_fig5 │ │ ├── train_endo.py │ │ └── ts_endo.xyz │ └── r4_tab1 │ │ ├── train.py │ │ └── ts.xyz ├── umbrella │ ├── irc.xyz │ ├── sn2.xyz │ ├── train.py │ └── umbrella.py ├── water.py ├── water.xyz ├── water_openmm.py └── water_openmm_colab.ipynb ├── install_ace.sh ├── install_gap.sh ├── install_mace.sh ├── mlptrain ├── __init__.py ├── box.py ├── config.py ├── configurations │ ├── __init__.py │ ├── calculate.py │ ├── configuration.py │ ├── configuration_set.py │ ├── plotting.py │ └── trajectory.py ├── descriptor │ ├── __init__.py │ ├── _base.py │ └── soap_descriptor.py ├── energy.py ├── forces.py ├── log.py ├── loss │ ├── __init__.py │ ├── _base.py │ ├── mean_errors.py │ └── tau.py ├── molecule.py ├── potentials │ ├── __init__.py │ ├── _base.py │ ├── ace │ │ ├── __init__.py │ │ └── ace.py │ ├── gap │ │ ├── __init__.py │ │ └── gap.py │ ├── mace │ │ ├── __init__.py │ │ └── mace.py │ └── nequip │ │ ├── __init__.py │ │ └── _nequip.py ├── sampling │ ├── __init__.py │ ├── _base.py │ ├── bias.py │ ├── md.py │ ├── md_openmm.py │ ├── metadynamics.py │ ├── plumed.py │ ├── reaction_coord.py │ └── umbrella.py ├── system.py ├── training │ ├── __init__.py │ ├── active.py │ └── selection.py └── utils.py ├── pyproject.toml └── tests ├── __init__.py ├── conftest.py ├── data ├── __init__.py ├── data.zip ├── methane.xyz ├── utils.py └── water_01_preplumed.npz ├── test_bias.py ├── test_calculate.py ├── test_configuration.py ├── test_configuration_set.py ├── test_descriptor.py ├── test_md.py ├── test_metadynamics.py ├── test_openmm_md.py ├── test_plotting.py ├── test_plumed.py ├── test_potential.py ├── test_reaction_coord.py ├── test_selection.py ├── test_system.py ├── test_trajectory.py ├── test_umbrella.py └── test_wham.py /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | a5e89e407dd5b4ac988138af6870262d3a9e43fa # apply ruff formatter 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "monthly" 9 | groups: 10 | gha-dependencies: 11 | patterns: 12 | - '*' 13 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: "Lint" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | FORCE_COLOR: 1 9 | 10 | jobs: 11 | pre-commit: 12 | env: 13 | SKIP: 'no-commit-to-branch' 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Run pre-commit 21 | uses: pre-commit/action@v3.0.1 22 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: pytest 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | 9 | env: 10 | FORCE_COLOR: 1 11 | 12 | jobs: 13 | test: 14 | name: GAP (${{ matrix.python-version }}, ${{ matrix.os }}) 15 | runs-on: ${{ matrix.os }} 16 | 17 | strategy: 18 | fail-fast: true 19 | matrix: 20 | os: ["ubuntu-22.04"] 21 | python-version: ["3.9"] 22 | 23 | defaults: 24 | run: 25 | shell: bash -l {0} 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - uses: mamba-org/setup-micromamba@v2 31 | with: 32 | # the create command looks like this: 33 | # `micromamba create -n test-env python=3.9 -f environment.yml` 34 | environment-file: environment.yml 35 | environment-name: gha-test-env 36 | cache-environment: true 37 | create-args: >- 38 | python=${{ matrix.python-version }} 39 | 40 | - name: Basic install 41 | run: ./install_gap.sh 42 | 43 | - name: Test basic install 44 | run: pytest --cov=mlptrain -k "not test_openmm" 45 | 46 | - name: Upload coverage reports to Codecov 47 | uses: codecov/codecov-action@v5 48 | with: 49 | flags: python-${{ matrix.python-version }} 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | slug: duartegroup/mlp-train 52 | 53 | test-ace: 54 | name: ACE (${{ matrix.python-version }}, ${{ matrix.os }}) 55 | runs-on: ${{ matrix.os }} 56 | 57 | strategy: 58 | fail-fast: true 59 | matrix: 60 | os: ["ubuntu-22.04"] 61 | python-version: ["3.9"] 62 | 63 | defaults: 64 | run: 65 | shell: bash -l {0} 66 | 67 | steps: 68 | - uses: actions/checkout@v4 69 | 70 | - uses: julia-actions/setup-julia@v2 71 | with: 72 | version: '1.6' 73 | 74 | - uses: mamba-org/setup-micromamba@v2 75 | with: 76 | environment-file: environment_ace.yml 77 | environment-name: gha-test-env 78 | cache-environment: true 79 | create-args: >- 80 | python=${{ matrix.python-version }} 81 | 82 | - name: ACE install 83 | run: ./install_ace.sh 84 | 85 | - name: Test ACE 86 | run: pytest --cov=mlptrain -k "not test_openmm" 87 | 88 | - name: Upload coverage reports to Codecov 89 | uses: codecov/codecov-action@v5 90 | with: 91 | flags: python-${{ matrix.python-version }}-ace 92 | token: ${{ secrets.CODECOV_TOKEN }} 93 | slug: duartegroup/mlp-train 94 | 95 | test-mace: 96 | name: MACE (${{ matrix.python-version }}, ${{ matrix.os }}) 97 | runs-on: ${{ matrix.os }} 98 | 99 | strategy: 100 | fail-fast: true 101 | matrix: 102 | os: ["ubuntu-22.04"] 103 | python-version: ["3.9"] 104 | 105 | defaults: 106 | run: 107 | shell: bash -l {0} 108 | 109 | steps: 110 | - uses: actions/checkout@v4 111 | 112 | - uses: mamba-org/setup-micromamba@v2 113 | with: 114 | environment-file: environment_mace.yml 115 | environment-name: gha-test-env 116 | cache-environment: true 117 | create-args: >- 118 | python=${{ matrix.python-version }} 119 | 120 | - name: MACE install 121 | run: ./install_mace.sh 122 | 123 | - name: Test MACE install 124 | run: pytest --cov=mlptrain 125 | 126 | - name: Upload coverage reports to Codecov 127 | uses: codecov/codecov-action@v5 128 | with: 129 | flags: python-${{ matrix.python-version }}-mace 130 | token: ${{ secrets.CODECOV_TOKEN }} 131 | slug: duartegroup/mlp-train 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | docs/_build/ 4 | .idea/ 5 | *.egg-info/ 6 | *DS_store 7 | **/__pycache__/ 8 | **/__MACOSX/ 9 | .coverage* 10 | .pytest_cache 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | # all hooks should run with python 3.6+ 3 | python: python3 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v4.5.0 7 | hooks: 8 | - id: no-commit-to-branch 9 | - id: check-executables-have-shebangs 10 | - id: check-shebang-scripts-are-executable 11 | exclude: examples/WTMetaD_paper/r2/free_energy/wtmetad_ib/accumulated_bias/bias_after_iter_15.dat 12 | - id: check-added-large-files 13 | args: ['--maxkb=500', '--enforce-all'] 14 | exclude: 'tests/data/data.zip|docs/*' 15 | - id: check-yaml 16 | - id: check-toml 17 | 18 | - repo: https://github.com/astral-sh/ruff-pre-commit 19 | # Ruff version. 20 | rev: v0.2.1 21 | hooks: 22 | - id: ruff 23 | args: [--show-source, --fix] 24 | - id: ruff-format 25 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: "ubuntu-22.04" 5 | tools: 6 | python: "mambaforge-22.9" 7 | 8 | conda: 9 | environment: environment.yml 10 | 11 | python: 12 | install: 13 | - method: pip 14 | path: . 15 | extra_requirements: 16 | - docs 17 | 18 | sphinx: 19 | configuration: docs/conf.py 20 | 21 | # Optionally build your docs in additional formats such as PDF and ePub 22 | # formats: 23 | # - pdf 24 | # - epub 25 | 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2021 Tom Young 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /create_conda_environment.sh: -------------------------------------------------------------------------------- 1 | # NOTE: This script should not be called on its own, 2 | # but should be sourced from other install scripts such as install_ace.sh 3 | set -euo pipefail 4 | 5 | if [[ -z ${CONDA_ENV_NAME} || -z ${CONDA_ENV_FILE} ]];then 6 | echo "ERROR: Please pass in conda environment name as the first parameter" 7 | echo "ERROR: Please pass in conda environment file as the second parameter" 8 | exit 1 9 | fi 10 | 11 | if [[ ! -f ${CONDA_ENV_FILE} ]];then 12 | echo "ERROR: File ${CONDA_ENV_FILE} does not exist" 13 | exit 1 14 | fi 15 | 16 | echo "* Looking for mamba or conda executable *" 17 | if which mamba; then 18 | export CONDA_EXE=mamba 19 | elif which micromamba; then 20 | export CONDA_EXE=micromamba 21 | elif which conda; then 22 | export CONDA_EXE=conda 23 | else 24 | echo "* ERROR: conda executable not found! *" 25 | exit 1 26 | fi 27 | 28 | if [[ ${CONDA_DEFAULT_ENV-} != "gha-test-env" ]];then 29 | echo "Installing everything to a new conda environment called: $CONDA_ENV_NAME" 30 | $CONDA_EXE env create -n "${CONDA_ENV_NAME}" --file ${CONDA_ENV_FILE} 31 | else 32 | CONDA_ENV_NAME="gha-test-env" 33 | # On GitHub the environment is auto-created by setup-micromamba action 34 | echo "* Skipping conda install, we're on Github, it's already there! *" 35 | fi 36 | 37 | echo "* Installing mlptrain package in editable mode *" 38 | 39 | if [[ ${CONDA_EXE} = "mamba" ]];then 40 | # For some reason `mamba run` does not seem to work, use conda instead. 41 | CONDA_EXE=conda 42 | fi 43 | ${CONDA_EXE} run -n ${CONDA_ENV_NAME} python3 -m pip install -e . 44 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | project = 'mlp-train' 10 | copyright = '2025, Duarte group' 11 | author = 'Duarte group' 12 | 13 | # -- General configuration --------------------------------------------------- 14 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 15 | 16 | extensions = [ 17 | 'sphinx_rtd_theme', 18 | 'sphinx.ext.autodoc', 19 | 'sphinx.ext.napoleon', 20 | 'sphinx_copybutton', 21 | ] 22 | 23 | templates_path = ['templates'] 24 | exclude_patterns = ['source', 'Thumbs.db', '.DS_Store'] 25 | 26 | 27 | # -- Options for HTML output ------------------------------------------------- 28 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 29 | 30 | html_theme = 'sphinx_rtd_theme' 31 | html_static_path = ['static'] 32 | -------------------------------------------------------------------------------- /docs/dev/installation.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Installation 3 | ************ 4 | 5 | Dependencies 6 | ------------ 7 | -------------------------------------------------------------------------------- /docs/examples/example1.rst: -------------------------------------------------------------------------------- 1 | *************************** 2 | Preparation of initial data 3 | *************************** 4 | 5 | Active learning cycle can be initiated from one structure (e.g., transition state) or from existing data sets in ``.npz`` or ``.xyz`` formats. 6 | 7 | --------------------------- 8 | Example 1: Loading xyz data 9 | --------------------------- 10 | 11 | You can train MLIP for existing dataset. The structures can be loaded in xyz format as: 12 | 13 | .. code-block:: python 14 | 15 | import mlptrain as mlt 16 | 17 | data = mlt.ConfigurationSet() 18 | data.load_xyz('data_set.xyz', charge = 0, mult = 1) 19 | 20 | 21 | This function assumes that all data in your dataset have the same charge and multiplicity. If it is not the case, you can load data with different charges separately. 22 | 23 | 24 | .. code-block:: python 25 | 26 | import mlptrain as mlt 27 | 28 | data = mlt.ConfigurationSet() 29 | data.load_xyz('water.xyz', charge = 0, mult = 1) 30 | data.load_xyz('magnesium_aqua_complex.xyz', charge = -2, mult = 1) 31 | 32 | This would allow you to compute *ab initio* labels with correct charges/multiplicities for your data. 33 | 34 | If you already have data labeled by reference, this can be loaded as well. However, the data need to be provided in extended xyz format, such as:: 35 | 36 | 2 37 | Lattice="100.000000 0.000000 0.000000 0.000000 100.000000 0.000000 0.000000 0.000000 100.000000" 38 | energy=-11581.02323085 Properties=species:S:1:pos:R:3:forces:R:3 39 | C 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 40 | O 1.00000 1.00000 1.00000 -1.00000 1.00000 1.00000 41 | 42 | where energy and forces are reference data in eV. 43 | 44 | You can than load the energies and forces as: 45 | 46 | .. code-block:: python 47 | 48 | import mlptrain as mlt 49 | 50 | data = mlt.ConfigurationSet() 51 | data.load_xyz('data_set.xyz', charge = 0, mult = 1, load_energies = True, load_forces = True) 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/examples/example2.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Example2 3 | ******** 4 | -------------------------------------------------------------------------------- /docs/examples/example3.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Example3 3 | ******** 4 | -------------------------------------------------------------------------------- /docs/images/AL_scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/docs/images/AL_scheme.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. This page is orphan because it is the documentation entry point 4 | 5 | 6 | mlp-train documentation 7 | ======================= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :hidden: 12 | :caption: Documentation 13 | 14 | dev/installation 15 | dev/contributing 16 | references/index 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | :hidden: 21 | :caption: Tutorials 22 | 23 | tutorials/tutorial1 24 | tutorials/tutorial2 25 | tutorials/tutorial3 26 | 27 | .. toctree:: 28 | :maxdepth: 2 29 | :hidden: 30 | :caption: Examples 31 | 32 | examples/example1 33 | examples/example2 34 | examples/example3 35 | 36 | .. toctree:: 37 | :maxdepth: 2 38 | :hidden: 39 | :caption: Citation 40 | 41 | 42 | *mlp-train* is Python package for training machine learning based interatomic potential (MLIP) developed by Duarte group at the University of Oxford. It performs active learning (AL) loop to generate training data set, followed by training of selected MLIP. 43 | 44 | .. image:: images/AL_scheme.png 45 | :align: center 46 | :width: 400 47 | :alt: Figure of summary of active learning cycle 48 | 49 | | 50 | 51 | .. note:: 52 | 53 | This documentation is under development. Stay tuned for more functionalities! 54 | 55 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/references/box.rst: -------------------------------------------------------------------------------- 1 | *** 2 | Box 3 | *** 4 | 5 | .. automodule:: mlptrain.box 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/configurations/calculate.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | Calculate 3 | ********* 4 | 5 | .. automodule:: mlptrain.configurations.calculate 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/configurations/configuration.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | Configuration 3 | ************* 4 | 5 | .. automodule:: mlptrain.configurations.configuration 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/configurations/configuration_set.rst: -------------------------------------------------------------------------------- 1 | **************** 2 | ConfigurationSet 3 | **************** 4 | 5 | .. automodule:: mlptrain.configurations.configuration_set 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/configurations/index.rst: -------------------------------------------------------------------------------- 1 | ************** 2 | Configurations 3 | ************** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | calculate 9 | configuration 10 | configuration_set 11 | plotting 12 | trajectory 13 | -------------------------------------------------------------------------------- /docs/references/configurations/plotting.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Plotting 3 | ******** 4 | 5 | .. automodule:: mlptrain.configurations.plotting 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/configurations/trajectory.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | Trajectory 3 | ********** 4 | 5 | .. automodule:: mlptrain.configurations.trajectory 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/descriptor/_base.rst: -------------------------------------------------------------------------------- 1 | *********** 2 | _base 3 | *********** 4 | 5 | .. automodule:: mlptrain.descriptor._base 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/descriptor/index.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | Descriptor 3 | ********** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | _base 9 | soap_descriptor 10 | -------------------------------------------------------------------------------- /docs/references/descriptor/soap_descriptor.rst: -------------------------------------------------------------------------------- 1 | *********** 2 | SOAP descriptor 3 | *********** 4 | 5 | .. automodule:: mlptrain.descriptor.soap_descriptor 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/energy.rst: -------------------------------------------------------------------------------- 1 | ****** 2 | Energy 3 | ****** 4 | 5 | .. automodule:: mlptrain.energy 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/forces.rst: -------------------------------------------------------------------------------- 1 | ****** 2 | Forces 3 | ****** 4 | 5 | .. automodule:: mlptrain.forces 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/index.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | References 3 | ********** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | box 9 | configurations/index 10 | descriptor/index 11 | energy 12 | forces 13 | loss/index 14 | molecule 15 | potentials/index 16 | sampling/index 17 | system 18 | training/index 19 | utils 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/references/loss/_base.rst: -------------------------------------------------------------------------------- 1 | ***** 2 | _base 3 | ***** 4 | 5 | .. automodule:: mlptrain.loss._base 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/loss/index.rst: -------------------------------------------------------------------------------- 1 | **** 2 | Loss 3 | **** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | _base 9 | mean_errors 10 | tau 11 | -------------------------------------------------------------------------------- /docs/references/loss/mean_errors.rst: -------------------------------------------------------------------------------- 1 | *********** 2 | Mean_errors 3 | *********** 4 | 5 | .. automodule:: mlptrain.loss.mean_errors 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/loss/tau.rst: -------------------------------------------------------------------------------- 1 | *** 2 | Tau 3 | *** 4 | 5 | .. automodule:: mlptrain.loss.tau 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/molecule.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Molecule 3 | ******** 4 | 5 | .. automodule:: mlptrain.molecule 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/potentials/_base.rst: -------------------------------------------------------------------------------- 1 | ***** 2 | _base 3 | ***** 4 | 5 | .. automodule:: mlptrain.potentials._base 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/potentials/ace.rst: -------------------------------------------------------------------------------- 1 | *** 2 | ACE 3 | *** 4 | 5 | .. automodule:: mlptrain.potentials.ace.ace 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/potentials/gap.rst: -------------------------------------------------------------------------------- 1 | *** 2 | GAP 3 | *** 4 | 5 | .. automodule:: mlptrain.potentials.gap.gap 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/potentials/index.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | Potentials 3 | ********** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | _base 9 | ace 10 | gap 11 | mace 12 | -------------------------------------------------------------------------------- /docs/references/potentials/mace.rst: -------------------------------------------------------------------------------- 1 | **** 2 | MACE 3 | **** 4 | 5 | .. automodule:: mlptrain.potentials.mace.mace 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/_base.rst: -------------------------------------------------------------------------------- 1 | ***** 2 | _Base 3 | ***** 4 | 5 | .. automodule:: mlptrain.sampling._base 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/bias.rst: -------------------------------------------------------------------------------- 1 | **** 2 | Bias 3 | **** 4 | 5 | .. automodule:: mlptrain.sampling.bias 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/index.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Sampling 3 | ******** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | _base 9 | bias 10 | md 11 | md_openmm 12 | metadynamics 13 | plumed 14 | reaction_coord 15 | umbrella 16 | -------------------------------------------------------------------------------- /docs/references/sampling/md.rst: -------------------------------------------------------------------------------- 1 | ** 2 | MD 3 | ** 4 | 5 | .. automodule:: mlptrain.sampling.md 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/md_openmm.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | MD_openmm 3 | ********* 4 | 5 | .. automodule:: mlptrain.sampling.md_openmm 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/metadynamics.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Metadynamics 3 | ************ 4 | 5 | .. automodule:: mlptrain.sampling.metadynamics 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/plumed.rst: -------------------------------------------------------------------------------- 1 | ****** 2 | Plumed 3 | ****** 4 | 5 | .. automodule:: mlptrain.sampling.plumed 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/reaction_coord.rst: -------------------------------------------------------------------------------- 1 | ************** 2 | Reaction_coord 3 | ************** 4 | 5 | .. automodule:: mlptrain.sampling.reaction_coord 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/sampling/umbrella.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Umbrella 3 | ******** 4 | 5 | .. automodule:: mlptrain.sampling.umbrella 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/system.rst: -------------------------------------------------------------------------------- 1 | ****** 2 | System 3 | ****** 4 | 5 | .. automodule:: mlptrain.system 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/references/training/active.rst: -------------------------------------------------------------------------------- 1 | Active 2 | ======================== 3 | 4 | .. automodule:: mlptrain.training.active 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/references/training/index.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | Training 3 | ******** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | active 9 | selection 10 | -------------------------------------------------------------------------------- /docs/references/training/selection.rst: -------------------------------------------------------------------------------- 1 | Selection 2 | =========================== 3 | 4 | .. automodule:: mlptrain.training.selection 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/references/utils.rst: -------------------------------------------------------------------------------- 1 | ***** 2 | Utils 3 | ***** 4 | 5 | .. automodule:: mlptrain.utils 6 | :members: 7 | :undoc-members: 8 | :show-inheritance: 9 | -------------------------------------------------------------------------------- /docs/tutorials/tutorial1.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | Tutorial1 3 | ********* 4 | -------------------------------------------------------------------------------- /docs/tutorials/tutorial2.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | Tutorial2 3 | ********* 4 | -------------------------------------------------------------------------------- /docs/tutorials/tutorial3.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | Tutorial3 3 | ********* 4 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | # Usage: mamba env create -n myenvname -f environment.yml 2 | --- 3 | name: mlptrain 4 | channels: 5 | - conda-forge 6 | dependencies: 7 | - python=3.9 8 | - libgfortran=14.* 9 | - pip 10 | - pre-commit 11 | - ase 12 | - autode=1.3.3 13 | - coloredlogs 14 | - cython 15 | - dscribe=2.0 16 | - matplotlib-base 17 | - numpy<2 18 | - pytest=8 19 | - pytest-cov=5 20 | - py-plumed 21 | - scipy 22 | - xtb 23 | - scikit-learn 24 | - pip: 25 | - quippy-ase # GAP 26 | - ase@git+https://gitlab.com/ase/ase.git@f2615a6e9a # For PLUMED 27 | -------------------------------------------------------------------------------- /environment_ace.yml: -------------------------------------------------------------------------------- 1 | # Usage: mamba env create -n myenvname -f environment_ace.yml 2 | --- 3 | name: mlptrain-ace 4 | channels: 5 | - conda-forge 6 | dependencies: 7 | - python=3.9 8 | - libgfortran=14.* 9 | - pip 10 | - pre-commit 11 | - ase 12 | - autode=1.3.3 13 | - coloredlogs 14 | - cython 15 | - dscribe=2.0 16 | - matplotlib-base 17 | - numpy<2 18 | - pytest=8 19 | - pytest-cov=5 20 | - py-plumed 21 | - scipy 22 | - xtb 23 | - scikit-learn 24 | - pip: 25 | - julia # Python-Julia integration (this will not install Julia itself!) 26 | - pyjulip@git+https://github.com/casv2/pyjulip.git@72280a6ac3 # Integration with ACE 27 | - ase@git+https://gitlab.com/ase/ase.git@f2615a6e9a # For PLUMED 28 | -------------------------------------------------------------------------------- /environment_mace.yml: -------------------------------------------------------------------------------- 1 | # Usage: mamba env create -n myenvname -f environment_mace.yml 2 | --- 3 | name: mlptrain-mace 4 | channels: 5 | - conda-forge 6 | - pytorch 7 | dependencies: 8 | - python=3.9 9 | - pip 10 | - libgfortran=14.* 11 | - pre-commit 12 | - ase 13 | - autode=1.3.3 14 | - coloredlogs 15 | - cython 16 | - dscribe=2.0 17 | - matplotlib-base 18 | - numpy 19 | - pytest=8 20 | - pytest-cov=5 21 | - py-plumed 22 | - scipy 23 | - xtb 24 | - scikit-learn 25 | - openmm=8.1.2 26 | - openmm-torch=1.4 27 | - nnpops 28 | - openmm-ml=1.2 29 | - git 30 | - pip: 31 | - ase@git+https://gitlab.com/ase/ase.git@f2615a6e9a # For PLUMED 32 | - mace-torch==0.3.6 33 | -------------------------------------------------------------------------------- /examples/DA_paper/1d_fes/cis_endo_TS_wB97M.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.48551827153476 4.22046776301455 -0.76387019964855 4 | C -3.29706997853341 3.20800825403778 0.00560550135848 5 | C -2.40613152285159 2.20188143774424 0.39280166083415 6 | H -4.17193907669498 3.47732454004277 0.57922874323701 7 | C -1.25421626341582 2.28073881423334 -0.39134018674370 8 | H -2.62971697790748 1.40772166988209 1.08790738827116 9 | C -1.38578121697094 3.33888426662266 -1.27292693453425 10 | H -0.46531442710123 1.54577034081137 -0.40594438977404 11 | H -0.63060343945869 3.66681988004509 -1.97069031398589 12 | H -2.05245199670347 4.92740600649740 -0.04650437268625 13 | H -3.02666202824448 4.78295774452280 -1.51796301673495 14 | C -3.06504740042196 2.35793555636474 -2.60937979146807 15 | C -4.11128678715421 2.39158047287693 -1.69752866089065 16 | H -4.87265150897932 3.15276283427014 -1.79773583587097 17 | H -4.44050572009143 1.44897075672146 -1.28720724310498 18 | C -2.34020015940562 1.10203379410996 -2.81792271712678 19 | H -2.93852127963818 3.14151880876616 -3.34300331696286 20 | O -2.50386109363881 0.13973783317053 -2.08940214569736 21 | C -1.36442140742721 1.05457064210129 -3.97140892173567 22 | H -0.69600040010072 1.91533933637728 -3.94662341331746 23 | H -1.91361709328836 1.09471264172471 -4.91346276230566 24 | H -0.79009611473740 0.13380188786275 -3.92976894681267 25 | -------------------------------------------------------------------------------- /examples/DA_paper/1d_fes/fes.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | import numpy as np 3 | from mlptrain.box import Box 4 | from mlptrain.log import logger 5 | 6 | mlt.Config.n_cores = 10 7 | 8 | if __name__ == '__main__': 9 | us = mlt.UmbrellaSampling( 10 | zeta_func=mlt.AverageDistance((1, 12), (6, 11)), kappa=10 11 | ) 12 | temp = 300 13 | 14 | neb = mlt.ConfigurationSet() 15 | neb.load_xyz(filename='neb_optimised.xyz', charge=0, mult=1) 16 | 17 | irc = mlt.ConfigurationSet() 18 | for config in neb: 19 | config.box = Box([18.5, 18.5, 18.5]) 20 | irc.append(config) 21 | 22 | r112_reactant = np.linalg.norm( 23 | irc[0].atoms[1].coord - irc[0].atoms[12].coord 24 | ) 25 | r611_reactant = np.linalg.norm( 26 | irc[0].atoms[6].coord - irc[0].atoms[11].coord 27 | ) 28 | 29 | r112_product = np.linalg.norm( 30 | irc[-1].atoms[1].coord - irc[-1].atoms[12].coord 31 | ) 32 | r611_product = np.linalg.norm( 33 | irc[-1].atoms[6].coord - irc[-1].atoms[11].coord 34 | ) 35 | 36 | logger.info( 37 | f'average bond length in reactant is {(r112_reactant+r611_reactant)/2}' 38 | ) 39 | logger.info( 40 | f'average bond length in product is {(r112_product+r611_product)/2}' 41 | ) 42 | 43 | irc.reverse() # Go product -> reactant, the NEB path is from reactant -> product 44 | 45 | water_mol = mlt.Molecule(name='h2o.xyz') 46 | TS_mol = mlt.Molecule(name='cis_endo_TS_wB97M.xyz') 47 | 48 | system = mlt.System(TS_mol, box=Box([100, 100, 100])) 49 | system.add_molecules(water_mol, num=200) 50 | 51 | endo = mlt.potentials.ACE('endo_in_water_ace_wB97M', system) 52 | 53 | us.run_umbrella_sampling( 54 | irc, 55 | mlp=endo, 56 | temp=temp, 57 | interval=5, 58 | dt=0.5, 59 | n_windows=15, 60 | init_ref=1.55, 61 | final_ref=4, 62 | ps=10, 63 | ) 64 | us.save('wide_US') 65 | 66 | # Run a second, narrower US with a higher force constant 67 | us.kappa = 20 68 | us.run_umbrella_sampling( 69 | irc, 70 | mlp=endo, 71 | temp=temp, 72 | interval=5, 73 | dt=0.5, 74 | n_windows=15, 75 | init_ref=1.7, 76 | final_ref=2.5, 77 | ps=10, 78 | ) 79 | 80 | us.save('narrow_US') 81 | 82 | total_us = mlt.UmbrellaSampling.from_folders( 83 | 'wide_US', 'narrow_US', temp=temp 84 | ) 85 | total_us.wham() 86 | -------------------------------------------------------------------------------- /examples/DA_paper/1d_fes/h2o.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -0.54939 0.30127 0.01109 4 | H 0.43994 0.33972 -0.00783 5 | H -0.83876 1.15104 -0.40694 6 | -------------------------------------------------------------------------------- /examples/DA_paper/2D_pes/cis_endo_TS_water.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.40476782586864 4.17875804112660 -0.76197607125766 4 | C -3.26841425524092 3.19378961565156 -0.01300975603437 5 | C -2.42863996866412 2.13353863852462 0.35237216469329 6 | H -4.12089103051748 3.49921632188227 0.57581123727200 7 | C -1.26777899818959 2.17358683943626 -0.42564281028238 8 | H -2.69156678157680 1.34462373860189 1.04103862635255 9 | C -1.33444206691975 3.26558462857531 -1.26943594934398 10 | H -0.51053629294263 1.40536119817026 -0.45324464865770 11 | H -0.57611212941483 3.55954130105160 -1.97869305255776 12 | H -1.95095138713046 4.85526671627690 -0.02775253382742 13 | H -2.90992805412667 4.77838156812614 -1.51126194850108 14 | C -3.14418636915129 2.39112452212082 -2.63303678196406 15 | C -4.15118387150145 2.45049247647618 -1.67374429168357 16 | H -4.87990860847807 3.24709753813741 -1.73413912618045 17 | H -4.52120530216100 1.51885077273490 -1.27286937748333 18 | C -2.43373393869840 1.15476450688714 -2.87508116981260 19 | H -2.97454513813053 3.21014495612672 -3.31522369970220 20 | O -2.65909097957588 0.13068753520198 -2.22643400480114 21 | C -1.36001547079682 1.14232415876071 -3.93628221954884 22 | H -1.31354387353520 2.07618971490440 -4.49088655887259 23 | H -1.54290520279477 0.31577329143810 -4.62206240447813 24 | H -0.39726661888476 0.96584720158830 -3.45558549902854 25 | -------------------------------------------------------------------------------- /examples/DA_paper/readme.md: -------------------------------------------------------------------------------- 1 | # Modeling Chemical Processes in Explicit Solvents with Machine Learning Potential 2 | 3 | This folder includes codes and starting files for various computational tasks related to the paper "Modeling Chemical Processes in Explicit Solvents with Machine Learning Potentials" (https://chemrxiv.org/engage/chemrxiv/article-details/64a8085fba3e99daefab8f89). These tasks include training implicit and explicit potentials, calculating 2D potential energy surfaces, 1D free energy surfaces, uphill dynamics, and downhill dynamics. 4 | 5 | Please note that due to the large size of the training and validation data, as well as the uphill and downhill trajectories, these data are not included in the GitHub repository, but can be downloaded from Oxford University Research Archive (ORA) (https://ora.ox.ac.uk/objects/uuid:68b9b398-e839-4683-a0b4-9e026b2408a2). 6 | 7 | One of the objectives achieved in the paper is training a machine learning potential for the Diels-Alder reaction of CP and MVK in an explicit solvent. In order to illustrate the workflow of training such potentials, we provide an example using the endo reaction in explicit water: 8 | ![alt text](training.png) 9 | For detailed code information, please refer to the file "endo_ace_ex.py" located in the "training/explicit" directory. 10 | -------------------------------------------------------------------------------- /examples/DA_paper/training.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/examples/DA_paper/training.png -------------------------------------------------------------------------------- /examples/DA_paper/training/explicit/cis_endo_TS_wB97M.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.48551827153476 4.22046776301455 -0.76387019964855 4 | C -3.29706997853341 3.20800825403778 0.00560550135848 5 | C -2.40613152285159 2.20188143774424 0.39280166083415 6 | H -4.17193907669498 3.47732454004277 0.57922874323701 7 | C -1.25421626341582 2.28073881423334 -0.39134018674370 8 | H -2.62971697790748 1.40772166988209 1.08790738827116 9 | C -1.38578121697094 3.33888426662266 -1.27292693453425 10 | H -0.46531442710123 1.54577034081137 -0.40594438977404 11 | H -0.63060343945869 3.66681988004509 -1.97069031398589 12 | H -2.05245199670347 4.92740600649740 -0.04650437268625 13 | H -3.02666202824448 4.78295774452280 -1.51796301673495 14 | C -3.06504740042196 2.35793555636474 -2.60937979146807 15 | C -4.11128678715421 2.39158047287693 -1.69752866089065 16 | H -4.87265150897932 3.15276283427014 -1.79773583587097 17 | H -4.44050572009143 1.44897075672146 -1.28720724310498 18 | C -2.34020015940562 1.10203379410996 -2.81792271712678 19 | H -2.93852127963818 3.14151880876616 -3.34300331696286 20 | O -2.50386109363881 0.13973783317053 -2.08940214569736 21 | C -1.36442140742721 1.05457064210129 -3.97140892173567 22 | H -0.69600040010072 1.91533933637728 -3.94662341331746 23 | H -1.91361709328836 1.09471264172471 -4.91346276230566 24 | H -0.79009611473740 0.13380188786275 -3.92976894681267 25 | -------------------------------------------------------------------------------- /examples/DA_paper/training/explicit/h2o.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -0.54939 0.30127 0.01109 4 | H 0.43994 0.33972 -0.00783 5 | H -0.83876 1.15104 -0.40694 6 | -------------------------------------------------------------------------------- /examples/DA_paper/training/implicit/cis_endo_TS_water.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.40476782586864 4.17875804112660 -0.76197607125766 4 | C -3.26841425524092 3.19378961565156 -0.01300975603437 5 | C -2.42863996866412 2.13353863852462 0.35237216469329 6 | H -4.12089103051748 3.49921632188227 0.57581123727200 7 | C -1.26777899818959 2.17358683943626 -0.42564281028238 8 | H -2.69156678157680 1.34462373860189 1.04103862635255 9 | C -1.33444206691975 3.26558462857531 -1.26943594934398 10 | H -0.51053629294263 1.40536119817026 -0.45324464865770 11 | H -0.57611212941483 3.55954130105160 -1.97869305255776 12 | H -1.95095138713046 4.85526671627690 -0.02775253382742 13 | H -2.90992805412667 4.77838156812614 -1.51126194850108 14 | C -3.14418636915129 2.39112452212082 -2.63303678196406 15 | C -4.15118387150145 2.45049247647618 -1.67374429168357 16 | H -4.87990860847807 3.24709753813741 -1.73413912618045 17 | H -4.52120530216100 1.51885077273490 -1.27286937748333 18 | C -2.43373393869840 1.15476450688714 -2.87508116981260 19 | H -2.97454513813053 3.21014495612672 -3.31522369970220 20 | O -2.65909097957588 0.13068753520198 -2.22643400480114 21 | C -1.36001547079682 1.14232415876071 -3.93628221954884 22 | H -1.31354387353520 2.07618971490440 -4.49088655887259 23 | H -1.54290520279477 0.31577329143810 -4.62206240447813 24 | H -0.39726661888476 0.96584720158830 -3.45558549902854 25 | -------------------------------------------------------------------------------- /examples/DA_paper/training/implicit/endo_ace_im.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | from mlptrain.training.selection import AtomicEnvSimilarity 3 | 4 | mlt.Config.n_cores = 10 5 | mlt.Config.orca_keywords = [ 6 | 'wB97M-D3BJ', 7 | 'def2-TZVP', 8 | 'def2/J', 9 | 'RIJCOSX', 10 | 'EnGrad', 11 | 'CPCM(water)', 12 | ] 13 | 14 | if __name__ == '__main__': 15 | mol_TS = mlt.Molecule('cis_endo_TS_water.xyz') 16 | 17 | TS = mlt.Configuration(charge=0, mult=1) 18 | TS.atoms += mol_TS.atoms.copy() 19 | 20 | system = mlt.System(mol_TS, box=None) 21 | 22 | ace = mlt.potentials.ACE('endo_ace_wB97M_imwater', system=system) 23 | 24 | selector = AtomicEnvSimilarity() 25 | ace.al_train( 26 | method_name='orca', 27 | selection_method=selector, 28 | max_active_time=5000, 29 | fix_init_config=True, 30 | temp=300, 31 | ) 32 | 33 | trajectory = mlt.md.run_mlp_md( 34 | configuration=TS, mlp=ace, fs=200, temp=300, dt=0.5, interval=10 35 | ) 36 | 37 | trajectory.compare(ace, 'orca') 38 | -------------------------------------------------------------------------------- /examples/DA_paper/uphill/cis_endo_TS_wB97M.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.48551827153476 4.22046776301455 -0.76387019964855 4 | C -3.29706997853341 3.20800825403778 0.00560550135848 5 | C -2.40613152285159 2.20188143774424 0.39280166083415 6 | H -4.17193907669498 3.47732454004277 0.57922874323701 7 | C -1.25421626341582 2.28073881423334 -0.39134018674370 8 | H -2.62971697790748 1.40772166988209 1.08790738827116 9 | C -1.38578121697094 3.33888426662266 -1.27292693453425 10 | H -0.46531442710123 1.54577034081137 -0.40594438977404 11 | H -0.63060343945869 3.66681988004509 -1.97069031398589 12 | H -2.05245199670347 4.92740600649740 -0.04650437268625 13 | H -3.02666202824448 4.78295774452280 -1.51796301673495 14 | C -3.06504740042196 2.35793555636474 -2.60937979146807 15 | C -4.11128678715421 2.39158047287693 -1.69752866089065 16 | H -4.87265150897932 3.15276283427014 -1.79773583587097 17 | H -4.44050572009143 1.44897075672146 -1.28720724310498 18 | C -2.34020015940562 1.10203379410996 -2.81792271712678 19 | H -2.93852127963818 3.14151880876616 -3.34300331696286 20 | O -2.50386109363881 0.13973783317053 -2.08940214569736 21 | C -1.36442140742721 1.05457064210129 -3.97140892173567 22 | H -0.69600040010072 1.91533933637728 -3.94662341331746 23 | H -1.91361709328836 1.09471264172471 -4.91346276230566 24 | H -0.79009611473740 0.13380188786275 -3.92976894681267 25 | -------------------------------------------------------------------------------- /examples/DA_paper/uphill/h2o.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -0.54939 0.30127 0.01109 4 | H 0.43994 0.33972 -0.00783 5 | H -0.83876 1.15104 -0.40694 6 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/README.md: -------------------------------------------------------------------------------- 1 | # Active learning meets metadynamics: Automated workflow for reactive machine learning potentials 2 | 3 | This folder contains codes and initial files for various computational tasks discussed in the paper "Active learning meets metadynamics: Automated workflow for reactive machine learning potentials" ([https://chemrxiv.org/engage/chemrxiv/article-details/671fe54b98c8527d9ea7647a]). 4 | 5 | The manuscript demonstrates three reactions: the SN2 reaction in implicit solvent (referred to as r1), rearrangement reaction in the gas phase (r2), and glycosylation reaction in explicit solvent (r3). Python scripts for each reaction can be found in the corresponding folders. Access to training and testing data for all three reactions will soon be made available in the Oxford Research Archive (ORA), and the relevant DOI will be provided here. For immediate access to these datasets, please contact the corresponding author, Fernanda Duarte. 6 | 7 | For r1 and r2, active learning (AL) was utilised with both WTMetaD-IB and downhill sampling methods to train the potential. Input geometries (as *.xyz files) and training script are located within folders named after the training methods, such as al_downhill. 8 | 9 | In the case of r2, free energy calculations were performed using different enhanced sampling methods. All scripts can be found in the free_energy folder within the r2 directory. Within the free_energy folder, each enhanced sampling method is organised in separate folders containing Python scripts and necessary configuration files. 10 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r1/al_downhill/r1_ts.xyz: -------------------------------------------------------------------------------- 1 | 6 2 | Coordinates from ORCA-job sn2_nebts_NEB-CI_converged 3 | C 0.02394151244587 -0.02273675265530 0.05167818806821 4 | F -0.65299103371021 0.72656624059729 -1.73488992550113 5 | Cl 0.68562794924814 -0.78844305036752 1.89308430540763 6 | H 0.99880860408280 0.16036474652930 -0.38536247269303 7 | H -0.52740777250600 0.82166542425637 0.44671920342826 8 | H -0.52797925956060 -0.89741660836014 -0.27122929870995 -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r1/al_downhill/training.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 30 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad', 'CPCM(Water)'] 5 | 6 | if __name__ == '__main__': 7 | # Initialise the system 8 | system = mlt.System(mlt.Molecule('r1_ts.xyz', charge=-1, mult=1), box=None) 9 | 10 | # Define CV and attach an upper wall 11 | avg_r = mlt.PlumedAverageCV(name='avg_r', atom_groups=((0, 1), (0, 2))) 12 | avg_r.attach_upper_wall(location=2.5, kappa=1000) 13 | 14 | # Define CVs for extra information 15 | r_f = mlt.PlumedAverageCV(name='r_f', atom_groups=(0, 1)) 16 | r_cl = mlt.PlumedAverageCV(name='r_cl', atom_groups=(0, 2)) 17 | 18 | # Initialise PlumedBias 19 | bias = mlt.PlumedBias(cvs=(avg_r, r_f, r_cl)) 20 | 21 | # Define the potential and train using Downhill AL (fix_init_config=True) 22 | ace = mlt.potentials.ACE('r1_downhill', system=system) 23 | ace.al_train( 24 | method_name='orca', 25 | temp=500, 26 | n_configs_iter=5, 27 | max_active_iters=50, 28 | min_active_iters=20, 29 | fix_init_config=True, 30 | bias=bias, 31 | ) 32 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r1/al_wtmetad/ch3cl_f.xyz: -------------------------------------------------------------------------------- 1 | 6 2 | 3 | C 0.11150 0.23091 0.58288 4 | F -0.88850 1.32101 -2.02704 5 | Cl 0.64512 -0.39439 2.08233 6 | H 1.02099 0.50449 -0.09172 7 | H -0.45901 1.13911 0.74042 8 | H -0.45901 -0.53348 0.04182 -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r1/al_wtmetad/training.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 20 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad', 'CPCM(Water)'] 5 | 6 | if __name__ == '__main__': 7 | system = mlt.System( 8 | mlt.Molecule('ch3cl_f.xyz', charge=-1, mult=1), box=None 9 | ) 10 | 11 | # Define CV and attach an upper wall 12 | avg_r = mlt.PlumedAverageCV(name='avg_r', atom_groups=((0, 1), (0, 2))) 13 | avg_r.attach_upper_wall(location=2.5, kappa=1000) 14 | 15 | # Define CVs for extra information 16 | r_f = mlt.PlumedAverageCV(name='r_f', atom_groups=(0, 1)) 17 | r_cl = mlt.PlumedAverageCV(name='r_cl', atom_groups=(0, 2)) 18 | 19 | # Define CV for WTMetaD AL (r_cl - r_f) 20 | diff_r = mlt.PlumedDifferenceCV( 21 | name='diff_r', atom_groups=((0, 2), (0, 1)) 22 | ) 23 | 24 | # Initialise PlumedBias for WTMetaD AL 25 | bias = mlt.PlumedBias(cvs=(avg_r, r_f, r_cl, diff_r)) 26 | bias.initialise_for_metad_al(width=0.05, cvs=diff_r, biasfactor=100) 27 | 28 | # Define the potential and train using WTMetaD AL (inherit_metad_bias=True) 29 | ace = mlt.potentials.ACE('r1_wtmetad', system=system) 30 | ace.al_train( 31 | method_name='orca', 32 | temp=300, 33 | n_init_configs=5, 34 | n_configs_iter=5, 35 | max_active_iters=50, 36 | min_active_iters=30, 37 | inherit_metad_bias=True, 38 | bias=bias, 39 | ) 40 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/al_downhill/al_downhill.py: -------------------------------------------------------------------------------- 1 | import autode as ade 2 | import mlptrain as mlt 3 | 4 | # ORCA PATH (change accordingly) 5 | ade.Config.ORCA.path = '/usr/local/orca_5_0_3/orca' 6 | 7 | mlt.Config.n_cores = 10 8 | mlt.Config.orca_keywords = ['PBE0', 'D3BJ', 'def2-SVP', 'EnGrad'] 9 | 10 | if __name__ == '__main__': 11 | system = mlt.System(mlt.Molecule('ts_r2.xyz', charge=0, mult=1), box=None) 12 | 13 | # Define CVs for extra information 14 | avg_r = mlt.PlumedAverageCV(name='avg_r', atom_groups=((14, 11), (14, 10))) 15 | r_1 = mlt.PlumedAverageCV(name='r_1', atom_groups=(14, 11)) 16 | r_2 = mlt.PlumedAverageCV(name='r_2', atom_groups=(14, 10)) 17 | diff_r = mlt.PlumedDifferenceCV( 18 | name='diff_r', atom_groups=((14, 11), (14, 10)) 19 | ) 20 | 21 | # Initialise PlumedBias 22 | bias = mlt.PlumedBias(cvs=(avg_r, r_1, r_2, diff_r)) 23 | 24 | # Define the potential and train using Downhill AL (fix_init_config=True) 25 | ace = mlt.potentials.ACE('r2_downhill', system=system) 26 | ace.al_train( 27 | method_name='orca', 28 | temp=500, 29 | n_init_configs=10, 30 | n_configs_iter=10, 31 | max_active_iters=50, 32 | min_active_iters=20, 33 | fix_init_config=True, 34 | bias=bias, 35 | ) 36 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/al_downhill/ts_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job ts_2 3 | C -2.66439005909114 2.65299207388647 0.78745998436255 4 | C -2.67887404989887 1.28747390765675 0.38852882506916 5 | C -1.56970666815882 0.67461786371524 -0.14641426702188 6 | C -0.37799042894344 1.42925812930736 -0.32382038603211 7 | C -0.37079634676906 2.81148083306055 0.09023385565607 8 | C -1.52125738464133 3.40795809751788 0.65254942674056 9 | H -1.50289375312190 4.45626539206397 0.96302768555986 10 | H -3.57070364836348 3.09792166406850 1.20441075664968 11 | H -3.60062126935202 0.71302763969736 0.51535519693843 12 | H -1.60258355173816 -0.37766602828367 -0.43967208064813 13 | C 0.92456570544541 3.31753505809060 -0.15347633687560 14 | C 1.72786411538397 2.23585108850567 -0.72575815854290 15 | H 1.22817413754548 4.36380165541877 -0.12613291191324 16 | C 0.88824740245950 1.09215485450898 -0.82943542615214 17 | C 2.18463150524301 2.44443139726686 1.05085740248514 18 | C 2.96854451026492 2.45002472048448 -1.54037911195286 19 | H 1.19994619177669 0.12279718501542 -1.21659289133211 20 | H 2.69452544821297 2.65557302244572 -2.58626430067515 21 | H 3.60551208150717 1.55243185555685 -1.53156185106088 22 | H 3.56503062112247 3.29922665297333 -1.17492648923220 23 | H 1.49069905669169 1.84916547328124 1.65040376618190 24 | H 2.42620003624003 3.40209098690239 1.53339211735138 25 | H 3.13306634818493 1.90539647685923 0.90501519444448 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/al_wtmetad/al_wtmetad.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'D3BJ', 'def2-SVP', 'EnGrad'] 5 | 6 | if __name__ == '__main__': 7 | system = mlt.System(mlt.Molecule('r_r2.xyz', charge=0, mult=1), box=None) 8 | 9 | # Define CV and attach an upper wall 10 | avg_r = mlt.PlumedAverageCV(name='avg_r', atom_groups=((14, 11), (14, 10))) 11 | avg_r.attach_upper_wall(location=2.5, kappa=1000) 12 | 13 | # Define CVs for extra information 14 | r_1 = mlt.PlumedAverageCV(name='r_1', atom_groups=(14, 11)) 15 | r_2 = mlt.PlumedAverageCV(name='r_2', atom_groups=(14, 10)) 16 | 17 | # Define CV for WTMetaD AL (r_1 - r_2) 18 | diff_r = mlt.PlumedDifferenceCV( 19 | name='diff_r', atom_groups=((14, 11), (14, 10)) 20 | ) 21 | 22 | # Initialise PlumedBias for WTMetaD AL 23 | bias = mlt.PlumedBias(cvs=(avg_r, r_1, r_2, diff_r)) 24 | bias.initialise_for_metad_al(width=0.05, cvs=diff_r, biasfactor=90) 25 | 26 | # Define the potential and train using WTMetaD AL (inherit_metad_bias=True) 27 | ace = mlt.potentials.ACE('isoindene_2_metad_2', system=system) 28 | ace.al_train( 29 | method_name='orca', 30 | temp=300, 31 | n_init_configs=5, 32 | n_configs_iter=10, 33 | max_active_iters=40, 34 | min_active_iters=20, 35 | inherit_metad_bias=True, 36 | bias=bias, 37 | ) 38 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/al_wtmetad/r_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job reactant_2 3 | C -2.81578879620079 2.71939195713069 0.40597652743383 4 | C -2.75306801675332 1.29879637349104 0.12494353708397 5 | C -1.57543956286648 0.67007495572890 -0.12725319077628 6 | C -0.35564336599371 1.43708094671903 -0.11453115671888 7 | C -0.41953214675013 2.88547229814566 0.17378132549330 8 | C -1.69985337085700 3.49383364072311 0.43197139067454 9 | H -1.76038527053415 4.56397504901440 0.64444557198421 10 | H -3.79415372095779 3.16615899050664 0.60025014318963 11 | H -3.68677075140821 0.73077207382657 0.11837866310995 12 | H -1.54169132169281 -0.40154984311682 -0.33807153245421 13 | C 0.83993724079311 3.39615214471678 0.13487848344868 14 | C 1.82185831002678 2.30200252248213 -0.18701816957288 15 | H 1.13022181850322 4.43459180951911 0.30501970498636 16 | C 0.94166848737729 1.08930300007155 -0.32657183576441 17 | C 2.83234594843814 2.11612229365514 0.95847709653150 18 | C 2.56148744849290 2.59927910422309 -1.50322929151805 19 | H 1.32235111859832 0.09373371278190 -0.56213839383041 20 | H 1.84979611856880 2.73365377204159 -2.33076142860886 21 | H 3.24183470046471 1.77249061784375 -1.76025485532520 22 | H 3.16292499408300 3.51688104975674 -1.40914717372226 23 | H 2.31566211120603 1.90230302413868 1.90547216568181 24 | H 3.43825007798258 3.02608548619431 1.09016398584808 25 | H 3.51580406856637 1.28144540475756 0.73822666157863 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/free_energy/umbrella/ts_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job ts_2 3 | C -2.66439005909114 2.65299207388647 0.78745998436255 4 | C -2.67887404989887 1.28747390765675 0.38852882506916 5 | C -1.56970666815882 0.67461786371524 -0.14641426702188 6 | C -0.37799042894344 1.42925812930736 -0.32382038603211 7 | C -0.37079634676906 2.81148083306055 0.09023385565607 8 | C -1.52125738464133 3.40795809751788 0.65254942674056 9 | H -1.50289375312190 4.45626539206397 0.96302768555986 10 | H -3.57070364836348 3.09792166406850 1.20441075664968 11 | H -3.60062126935202 0.71302763969736 0.51535519693843 12 | H -1.60258355173816 -0.37766602828367 -0.43967208064813 13 | C 0.92456570544541 3.31753505809060 -0.15347633687560 14 | C 1.72786411538397 2.23585108850567 -0.72575815854290 15 | H 1.22817413754548 4.36380165541877 -0.12613291191324 16 | C 0.88824740245950 1.09215485450898 -0.82943542615214 17 | C 2.18463150524301 2.44443139726686 1.05085740248514 18 | C 2.96854451026492 2.45002472048448 -1.54037911195286 19 | H 1.19994619177669 0.12279718501542 -1.21659289133211 20 | H 2.69452544821297 2.65557302244572 -2.58626430067515 21 | H 3.60551208150717 1.55243185555685 -1.53156185106088 22 | H 3.56503062112247 3.29922665297333 -1.17492648923220 23 | H 1.49069905669169 1.84916547328124 1.65040376618190 24 | H 2.42620003624003 3.40209098690239 1.53339211735138 25 | H 3.13306634818493 1.90539647685923 0.90501519444448 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/free_energy/umbrella/umbrella.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 15 4 | 5 | if __name__ == '__main__': 6 | system = mlt.System(mlt.Molecule('ts_r2.xyz', charge=0, mult=1), box=None) 7 | 8 | # Load IRC 9 | irc = mlt.ConfigurationSet() 10 | irc.load_xyz('r2_irc.xyz', charge=0, mult=1) 11 | 12 | # Load potential 13 | mlp = mlt.potentials.ACE('r2_wtmetad', system=system) 14 | 15 | # Define CV for umbrella sampling (r_1 - r_2) 16 | zeta_func = mlt.DifferenceDistance((14, 11), (14, 10)) 17 | 18 | # Define UmbrellaSampling object 19 | umbrella = mlt.UmbrellaSampling(zeta_func=zeta_func, kappa=20, temp=365.6) 20 | 21 | # Perform MLP umbrella sampling 22 | umbrella.run_umbrella_sampling( 23 | traj=irc, mlp=mlp, temp=365.6, interval=10, dt=0.5, n_windows=30, ps=40 24 | ) 25 | 26 | # Truncate first 10 ps from each window 27 | umbrella.truncate_window_trajectories(removed_fraction=0.25) 28 | 29 | # Compute free energy using WHAM 30 | umbrella.wham(n_bins=500) 31 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/free_energy/wtmetad/r_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job reactant_2 3 | C -2.81578879620079 2.71939195713069 0.40597652743383 4 | C -2.75306801675332 1.29879637349104 0.12494353708397 5 | C -1.57543956286648 0.67007495572890 -0.12725319077628 6 | C -0.35564336599371 1.43708094671903 -0.11453115671888 7 | C -0.41953214675013 2.88547229814566 0.17378132549330 8 | C -1.69985337085700 3.49383364072311 0.43197139067454 9 | H -1.76038527053415 4.56397504901440 0.64444557198421 10 | H -3.79415372095779 3.16615899050664 0.60025014318963 11 | H -3.68677075140821 0.73077207382657 0.11837866310995 12 | H -1.54169132169281 -0.40154984311682 -0.33807153245421 13 | C 0.83993724079311 3.39615214471678 0.13487848344868 14 | C 1.82185831002678 2.30200252248213 -0.18701816957288 15 | H 1.13022181850322 4.43459180951911 0.30501970498636 16 | C 0.94166848737729 1.08930300007155 -0.32657183576441 17 | C 2.83234594843814 2.11612229365514 0.95847709653150 18 | C 2.56148744849290 2.59927910422309 -1.50322929151805 19 | H 1.32235111859832 0.09373371278190 -0.56213839383041 20 | H 1.84979611856880 2.73365377204159 -2.33076142860886 21 | H 3.24183470046471 1.77249061784375 -1.76025485532520 22 | H 3.16292499408300 3.51688104975674 -1.40914717372226 23 | H 2.31566211120603 1.90230302413868 1.90547216568181 24 | H 3.43825007798258 3.02608548619431 1.09016398584808 25 | H 3.51580406856637 1.28144540475756 0.73822666157863 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/free_energy/wtmetad/wtmetad.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | 5 | if __name__ == '__main__': 6 | system = mlt.System(mlt.Molecule('r_r2.xyz', charge=0, mult=1), box=None) 7 | 8 | # Load reactant configuration 9 | config = mlt.ConfigurationSet() 10 | config.load_xyz('r_r2.xyz', charge=0, mult=1) 11 | config = config[0] 12 | 13 | # Define CVs for extra information 14 | r_1 = mlt.PlumedAverageCV(name='r_1', atom_groups=(14, 11)) 15 | r_2 = mlt.PlumedAverageCV(name='r_2', atom_groups=(14, 10)) 16 | 17 | # Define CV for WTMetaD (r_1 - r_2) 18 | diff_r = mlt.PlumedDifferenceCV( 19 | name='diff_r', atom_groups=((14, 11), (14, 10)) 20 | ) 21 | 22 | # Load potential 23 | ace = mlt.potentials.ACE('r2_wtmetad', system=system) 24 | 25 | # Initialise PlumedBias 26 | bias = mlt.PlumedBias(cvs=(r_1, r_2, diff_r)) 27 | 28 | # Initialise Metadynamics object 29 | metad = mlt.Metadynamics(cvs=diff_r, bias=bias) 30 | 31 | # Perform MLP well-tempered metadynamics 32 | metad.run_metadynamics( 33 | configuration=config, 34 | mlp=ace, 35 | temp=365.6, 36 | interval=10, 37 | dt=0.5, 38 | pace=200, 39 | biasfactor=50, 40 | n_runs=10, 41 | ps=500, 42 | ) 43 | 44 | # Plot FES convergence 45 | metad.plot_fes_convergence(stride=10) 46 | 47 | # Compute and plot FES 48 | metad.compute_fes(n_bins=500) 49 | metad.plot_fes() 50 | 51 | # Perform block analysis 52 | metad.block_analysis( 53 | start_time=200, temp=365.6, dt=0.5, interval=10, min_n_blocks=30 54 | ) 55 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/free_energy/wtmetad_ib/r_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job reactant_2 3 | C -2.81578879620079 2.71939195713069 0.40597652743383 4 | C -2.75306801675332 1.29879637349104 0.12494353708397 5 | C -1.57543956286648 0.67007495572890 -0.12725319077628 6 | C -0.35564336599371 1.43708094671903 -0.11453115671888 7 | C -0.41953214675013 2.88547229814566 0.17378132549330 8 | C -1.69985337085700 3.49383364072311 0.43197139067454 9 | H -1.76038527053415 4.56397504901440 0.64444557198421 10 | H -3.79415372095779 3.16615899050664 0.60025014318963 11 | H -3.68677075140821 0.73077207382657 0.11837866310995 12 | H -1.54169132169281 -0.40154984311682 -0.33807153245421 13 | C 0.83993724079311 3.39615214471678 0.13487848344868 14 | C 1.82185831002678 2.30200252248213 -0.18701816957288 15 | H 1.13022181850322 4.43459180951911 0.30501970498636 16 | C 0.94166848737729 1.08930300007155 -0.32657183576441 17 | C 2.83234594843814 2.11612229365514 0.95847709653150 18 | C 2.56148744849290 2.59927910422309 -1.50322929151805 19 | H 1.32235111859832 0.09373371278190 -0.56213839383041 20 | H 1.84979611856880 2.73365377204159 -2.33076142860886 21 | H 3.24183470046471 1.77249061784375 -1.76025485532520 22 | H 3.16292499408300 3.51688104975674 -1.40914717372226 23 | H 2.31566211120603 1.90230302413868 1.90547216568181 24 | H 3.43825007798258 3.02608548619431 1.09016398584808 25 | H 3.51580406856637 1.28144540475756 0.73822666157863 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/free_energy/wtmetad_ib/wtmetad_ib.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | 5 | if __name__ == '__main__': 6 | system = mlt.System(mlt.Molecule('r_r2.xyz', charge=0, mult=1), box=None) 7 | 8 | # Load reactant configuration 9 | config = mlt.ConfigurationSet() 10 | config.load_xyz('r_r2.xyz', charge=0, mult=1) 11 | config = config[0] 12 | 13 | # Define CVs for extra information 14 | r_1 = mlt.PlumedAverageCV(name='r_1', atom_groups=(14, 11)) 15 | r_2 = mlt.PlumedAverageCV(name='r_2', atom_groups=(14, 10)) 16 | 17 | # Define CV for WTMetaD (r_1 - r_2) 18 | diff_r = mlt.PlumedDifferenceCV( 19 | name='diff_r', atom_groups=((14, 11), (14, 10)) 20 | ) 21 | 22 | # Load potential 23 | ace = mlt.potentials.ACE('r2_wtmetad', system=system) 24 | 25 | # Initialise PlumedBias 26 | bias = mlt.PlumedBias(cvs=(r_1, r_2, diff_r)) 27 | 28 | # Initialise Metadynamics object 29 | metad = mlt.Metadynamics(cvs=diff_r, bias=bias) 30 | 31 | # Perform MLP well-tempered metadynamics 32 | # (load inherited bias with index 15) 33 | metad.run_metadynamics( 34 | configuration=config, 35 | mlp=ace, 36 | temp=365.6, 37 | interval=10, 38 | dt=0.5, 39 | pace=200, 40 | biasfactor=80, 41 | n_runs=10, 42 | al_iter=15, 43 | ps=250, 44 | ) 45 | 46 | # Compute and plot FES (via reweighting) 47 | metad.compute_fes( 48 | n_bins=500, 49 | via_reweighting=True, 50 | bandwidth=0.02, 51 | temp=365.6, 52 | dt=0.5, 53 | interval=10, 54 | ) 55 | metad.plot_fes() 56 | 57 | # Perform block analysis 58 | metad.block_analysis( 59 | start_time=0, temp=365.6, dt=0.5, interval=10, min_n_blocks=30 60 | ) 61 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/structure/p_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job product_2 3 | C -2.50799213406729 2.48564314344744 1.04526214650192 4 | C -2.72031098104488 1.28991918739582 0.35630205004126 5 | C -1.70711516454949 0.72145192851016 -0.41845079093431 6 | C -0.47584751763618 1.37211431098763 -0.49345308525425 7 | C -0.26526306861967 2.58249534163807 0.19672897764831 8 | C -1.27360198189651 3.14084087936072 0.96798570237894 9 | H -1.11442494958531 4.07826021314105 1.50827800738901 10 | H -3.31231498167588 2.91317704803534 1.64863112594427 11 | H -3.69065306570208 0.79204534119273 0.42633146179432 12 | H -1.87704378027897 -0.21617245956383 -0.95364483002228 13 | C 1.14130757900132 3.05844186457041 -0.06315343196484 14 | C 1.69752018076028 1.97849592827519 -0.97000026843584 15 | H 1.10184240748762 4.00541011329513 -0.63769423244056 16 | C 0.75646948274566 1.03613735017718 -1.20024677488082 17 | C 1.96106972821619 3.29706994742568 1.20363189971293 18 | C 3.08745404266553 2.01857649893330 -1.49818357763922 19 | H 0.88812812845174 0.15109294307433 -1.82643234904979 20 | H 3.27808674283871 2.95249070199586 -2.05384647599272 21 | H 3.28675030760806 1.17364759747112 -2.17187652785216 22 | H 3.82956774296273 1.98331768297836 -0.68232461718003 23 | H 2.03601113355492 2.37136282845997 1.79430295992526 24 | H 1.49152497702461 4.06569357143162 1.83520881857199 25 | H 2.97952303519522 3.63892511418347 0.96413920396582 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/structure/r_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job reactant_2 3 | C -2.81578879620079 2.71939195713069 0.40597652743383 4 | C -2.75306801675332 1.29879637349104 0.12494353708397 5 | C -1.57543956286648 0.67007495572890 -0.12725319077628 6 | C -0.35564336599371 1.43708094671903 -0.11453115671888 7 | C -0.41953214675013 2.88547229814566 0.17378132549330 8 | C -1.69985337085700 3.49383364072311 0.43197139067454 9 | H -1.76038527053415 4.56397504901440 0.64444557198421 10 | H -3.79415372095779 3.16615899050664 0.60025014318963 11 | H -3.68677075140821 0.73077207382657 0.11837866310995 12 | H -1.54169132169281 -0.40154984311682 -0.33807153245421 13 | C 0.83993724079311 3.39615214471678 0.13487848344868 14 | C 1.82185831002678 2.30200252248213 -0.18701816957288 15 | H 1.13022181850322 4.43459180951911 0.30501970498636 16 | C 0.94166848737729 1.08930300007155 -0.32657183576441 17 | C 2.83234594843814 2.11612229365514 0.95847709653150 18 | C 2.56148744849290 2.59927910422309 -1.50322929151805 19 | H 1.32235111859832 0.09373371278190 -0.56213839383041 20 | H 1.84979611856880 2.73365377204159 -2.33076142860886 21 | H 3.24183470046471 1.77249061784375 -1.76025485532520 22 | H 3.16292499408300 3.51688104975674 -1.40914717372226 23 | H 2.31566211120603 1.90230302413868 1.90547216568181 24 | H 3.43825007798258 3.02608548619431 1.09016398584808 25 | H 3.51580406856637 1.28144540475756 0.73822666157863 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r2/structure/ts_r2.xyz: -------------------------------------------------------------------------------- 1 | 23 2 | Coordinates from ORCA-job ts_2 3 | C -2.66439005909114 2.65299207388647 0.78745998436255 4 | C -2.67887404989887 1.28747390765675 0.38852882506916 5 | C -1.56970666815882 0.67461786371524 -0.14641426702188 6 | C -0.37799042894344 1.42925812930736 -0.32382038603211 7 | C -0.37079634676906 2.81148083306055 0.09023385565607 8 | C -1.52125738464133 3.40795809751788 0.65254942674056 9 | H -1.50289375312190 4.45626539206397 0.96302768555986 10 | H -3.57070364836348 3.09792166406850 1.20441075664968 11 | H -3.60062126935202 0.71302763969736 0.51535519693843 12 | H -1.60258355173816 -0.37766602828367 -0.43967208064813 13 | C 0.92456570544541 3.31753505809060 -0.15347633687560 14 | C 1.72786411538397 2.23585108850567 -0.72575815854290 15 | H 1.22817413754548 4.36380165541877 -0.12613291191324 16 | C 0.88824740245950 1.09215485450898 -0.82943542615214 17 | C 2.18463150524301 2.44443139726686 1.05085740248514 18 | C 2.96854451026492 2.45002472048448 -1.54037911195286 19 | H 1.19994619177669 0.12279718501542 -1.21659289133211 20 | H 2.69452544821297 2.65557302244572 -2.58626430067515 21 | H 3.60551208150717 1.55243185555685 -1.53156185106088 22 | H 3.56503062112247 3.29922665297333 -1.17492648923220 23 | H 1.49069905669169 1.84916547328124 1.65040376618190 24 | H 2.42620003624003 3.40209098690239 1.53339211735138 25 | H 3.13306634818493 1.90539647685923 0.90501519444448 26 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r3/CCl2.xyz: -------------------------------------------------------------------------------- 1 | 5 2 | 3 | C 1.4258330047 0.3522418460 0.0101187143 4 | Cl 3.2030476626 0.3031959593 -0.0081820485 5 | Cl 0.8794284693 2.0440819251 -0.0086321892 6 | H 1.0515363170 -0.1471074061 0.9273550462 7 | H 1.0321571449 -0.1734417947 -0.8839987967 8 | ~ 9 | -------------------------------------------------------------------------------- /examples/WTMetaD_paper/r3/gly_gas.xyz: -------------------------------------------------------------------------------- 1 | 54 2 | Energy: 79.9281420 3 | C 11.09708 8.65396 5.86066 4 | C 10.62726 8.22657 7.25799 5 | C 9.88034 6.88827 7.14530 6 | C 10.79908 5.85550 6.44475 7 | C 11.94060 7.52399 5.23181 8 | H 11.53964 8.06293 7.88729 9 | H 10.17584 8.79822 5.24239 10 | H 11.69747 5.66143 7.07567 11 | H 12.15155 7.82552 4.19128 12 | H 8.98796 7.04601 6.48848 13 | O 11.19504 6.31687 5.15832 14 | O 11.86851 9.83864 5.97517 15 | O 9.75482 9.21820 7.77759 16 | O 9.55100 6.42557 8.44642 17 | C 10.09256 4.50328 6.27573 18 | H 10.81292 3.80447 5.79121 19 | H 9.82125 4.11537 7.28451 20 | O 8.94323 4.66232 5.47059 21 | C 8.29185 3.42113 5.33384 22 | H 8.94037 2.67820 4.81864 23 | H 7.93359 3.04173 6.31651 24 | H 7.39597 3.56741 4.69620 25 | C 8.15542 6.43577 8.64767 26 | H 7.74135 7.46317 8.54602 27 | H 7.64892 5.72109 7.96198 28 | H 7.94882 6.10077 9.68454 29 | C 10.18897 9.66050 9.04377 30 | H 11.17769 10.16520 8.97119 31 | H 9.46282 10.40915 9.42112 32 | H 10.20862 8.82385 9.77675 33 | C 11.63852 10.69371 4.87772 34 | H 11.87081 10.19307 3.91228 35 | H 10.59869 11.08791 4.89325 36 | H 12.32224 11.56287 4.96352 37 | O 13.10877 7.32562 5.95780 38 | C 14.41233 7.49246 5.58904 39 | O 14.74957 7.93115 4.49184 40 | C 15.46839 7.12454 6.59683 41 | Cl 17.12347 7.40145 5.98141 42 | Cl 15.24351 8.10281 8.06791 43 | Cl 15.30524 5.40235 7.01925 44 | O 9.24262 8.25026 2.44598 45 | H 9.01056 8.34017 1.48438 46 | C 7.06471 7.27781 2.83257 47 | H 6.75603 7.38378 1.77065 48 | H 6.55147 6.39489 3.27016 49 | H 6.74592 8.18343 3.39092 50 | C 8.58293 7.11212 2.93981 51 | H 8.83568 7.01059 4.01466 52 | C 9.06783 5.85191 2.21705 53 | H 10.16594 5.74503 2.34402 54 | H 8.57449 4.95150 2.64013 55 | H 8.83549 5.90947 1.13218 56 | H 14.14686 8.19963 3.79029 57 | -------------------------------------------------------------------------------- /examples/da_ts.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('da_ts.xyz'), box=None) 9 | gap = mlt.potentials.GAP('da', system=system) 10 | 11 | gap.al_train( 12 | method_name='orca', 13 | temp=300, # K 14 | selection_method=mlt.selection.AtomicEnvSimilarity(), 15 | max_active_time=200, # fs 16 | fix_init_config=True, 17 | ) 18 | 19 | # Run some dynamics with the potential 20 | trajectory = mlt.md.run_mlp_md( 21 | configuration=system.configuration, 22 | mlp=gap, 23 | fs=300, 24 | temp=100, 25 | dt=0.5, 26 | interval=10, 27 | ) 28 | 29 | # and compare, plotting a parity plots and E_true, ∆E and ∆F 30 | trajectory.compare(gap, 'orca') 31 | -------------------------------------------------------------------------------- /examples/da_ts.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/inherited_bias_active_learning/h2/h2.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | 5 | if __name__ == '__main__': 6 | # Initialise the system to train 7 | h2_system = mlt.System(mlt.Molecule('h2.xyz'), box=None) 8 | 9 | # Define collective variables and attach upper walls 10 | # to restrain the system from exploding 11 | cv1 = mlt.PlumedAverageCV(name='cv1', atom_groups=(0, 1)) 12 | cv1.attach_upper_wall(location=5, kappa=10000) 13 | 14 | # Attach CVs to a bias and initialise it for metadynamics AL. 15 | 16 | # By default, metadynamics bias is stored as a list of deposited 17 | # gaussians, which results in every MD step scaling linearly with 18 | # the total length of the simulation. To make the scaling constant, 19 | # the bias can be stored on a grid. This requires to specify the 20 | # bounds for the grid, and the bounds should be chosen such that 21 | # during AL the system would not leave the grid (either by using 22 | # a large grid or attaching walls to constrain the system). 23 | 24 | # Other metadynamics parameters can also be set by the method, 25 | # see initialise_for_metad_al() documentation for more info. 26 | bias = mlt.PlumedBias(cvs=cv1) 27 | bias.initialise_for_metad_al(width=0.05, biasfactor=100) 28 | 29 | # Define the potential and train it using metadynamics inherited-bias AL. 30 | # Inheritance can be set to False, the same initialisation works. 31 | # Metadynamics bias starts being applied at iteration 2, at iterations 0 32 | # and 1 the training is performed using unbiased MD with the attached walls 33 | ace = mlt.potentials.ACE('hydrogen', system=h2_system) 34 | ace.al_train( 35 | method_name='xtb', 36 | temp=300, 37 | max_active_iters=50, 38 | min_active_iters=10, 39 | bias_start_iter=2, 40 | inherit_metad_bias=True, 41 | bias=bias, 42 | ) 43 | 44 | # NOTE: The same al_train() method works with arbitrary PLUMED biases 45 | # (i.e. not only metadynamics) by initialising a PlumedBias using a 46 | # PLUMED input file, but then inheritance is unavailable 47 | -------------------------------------------------------------------------------- /examples/inherited_bias_active_learning/h2/h2.xyz: -------------------------------------------------------------------------------- 1 | 2 2 | 3 | H 0 0 0 4 | H 0 0 1 -------------------------------------------------------------------------------- /examples/inherited_bias_active_learning/h2o/h2o.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | 5 | if __name__ == '__main__': 6 | # Initialise the system to train 7 | h2o_system = mlt.System(mlt.Molecule('h2o.xyz'), box=None) 8 | 9 | # Define collective variables and attach upper walls 10 | # to restrain the system from exploding 11 | cv1 = mlt.PlumedAverageCV(name='cv1', atom_groups=((0, 1), (0, 2))) 12 | cv1.attach_upper_wall(location=5, kappa=10000) 13 | 14 | cv2 = mlt.PlumedAverageCV(name='cv2', atom_groups=(1, 0, 2)) 15 | cv2.attach_upper_wall(location=3.14, kappa=10000) 16 | 17 | # Attach CVs to a bias and initialise it for metadynamics AL. 18 | 19 | # By default, metadynamics bias is stored as a list of deposited 20 | # gaussians, which results in every MD step scaling linearly with 21 | # the total length of the simulation. To make the scaling constant, 22 | # the bias can be stored on a grid. This requires to specify the 23 | # bounds for the grid, and the bounds should be chosen such that 24 | # during AL the system would not leave the grid (either by using 25 | # a large grid or attaching walls to constrain the system). 26 | 27 | # Other metadynamics parameters can also be set by the method, 28 | # see initialise_for_metad_al() documentation for more info. 29 | bias = mlt.PlumedBias(cvs=(cv1, cv2)) 30 | bias.initialise_for_metad_al(width=(0.05, 0.10), biasfactor=100) 31 | 32 | # Define the potential and train it using metadynamics inherited-bias AL. 33 | # Inheritance can be set to False, the same initialisation works. 34 | # Metadynamics bias starts being applied at iteration 2, at iterations 0 35 | # and 1 the training is performed using unbiased MD with the attached walls 36 | ace = mlt.potentials.ACE('water', system=h2o_system) 37 | ace.al_train( 38 | method_name='xtb', 39 | temp=300, 40 | max_active_iters=50, 41 | min_active_iters=5, 42 | bias_start_iter=2, 43 | inherit_metad_bias=True, 44 | bias=bias, 45 | ) 46 | 47 | # NOTE: The same al_train() method works with arbitrary PLUMED biases 48 | # (i.e. not only metadynamics) by initialising a PlumedBias using a 49 | # PLUMED input file, but then inheritance is unavailable 50 | -------------------------------------------------------------------------------- /examples/inherited_bias_active_learning/h2o/h2o.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -1.85501 -0.92269 0.00271 4 | H -0.86568 -0.96530 -0.00191 5 | H -2.14438 -1.86421 -0.09929 6 | -------------------------------------------------------------------------------- /examples/metadynamics/h2/h2.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 8 4 | 5 | if __name__ == '__main__': 6 | # Initialise the system 7 | h2_system = mlt.System(mlt.Molecule('h2.xyz'), box=None) 8 | 9 | # Generate a starting metadynamics configuration 10 | h2_config = h2_system.random_configuration() 11 | 12 | # Define CVs, can also attach walls to them. More complicated CVs (i.e. 13 | # not DISTANCE, ANGLE, or TORSION; e.g. PATH) can be defined using 14 | # PlumedCustomCV that requires a PLUMED-like input file containing the 15 | # definition of the CV 16 | cv1 = mlt.PlumedAverageCV(name='cv1', atom_groups=(0, 1)) 17 | cv1.attach_upper_wall(location=5, kappa=10000) 18 | 19 | # Train (or load) a machine learning potential 20 | ace = mlt.potentials.ACE(name='hydrogen', system=h2_system) 21 | 22 | # Active learning (can be commented out if the potential is loaded) 23 | al_bias = mlt.PlumedBias(cvs=cv1) 24 | al_bias.initialise_for_metad_al(width=0.05, biasfactor=100) 25 | ace.al_train( 26 | method_name='xtb', 27 | temp=300, 28 | max_active_iters=50, 29 | min_active_iters=10, 30 | bias_start_iter=2, 31 | inherit_metad_bias=True, 32 | bias=al_bias, 33 | ) 34 | 35 | # Attach CVs to the metadynamics object 36 | metad = mlt.Metadynamics(cvs=cv1) 37 | 38 | # Can run optional methods (estimate_width() and try_multiple_biafactors()) 39 | # to help choose appropriate metadynamics parameters (width and bias factor), 40 | width = metad.estimate_width(configurations=h2_config, mlp=ace, plot=True) 41 | 42 | metad.try_multiple_biasfactors( 43 | configuration=h2_config, 44 | mlp=ace, 45 | temp=300, 46 | interval=10, 47 | dt=1, 48 | width=width, 49 | biasfactors=(5, 10, 15), 50 | plotted_cvs=cv1, 51 | ps=20, 52 | ) 53 | 54 | # Execute metadynamics production runs, 8 independent simulations are 55 | # performed in parallel 56 | metad.run_metadynamics( 57 | configuration=h2_config, 58 | mlp=ace, 59 | temp=300, 60 | interval=10, 61 | dt=1, 62 | width=width, 63 | biasfactor=5, 64 | n_runs=8, 65 | restart=False, 66 | ps=20, 67 | ) 68 | 69 | # Plot the resulting free energy surface (FES), the same method can be used 70 | # to plot the FES from block analysis or FES from a previous simulation 71 | # using .npy file, for all the options see the documentation of plot_fes() 72 | metad.plot_fes() 73 | 74 | # Can run optional methods (plot_fes_convergence() and block_analysis()) 75 | # to estimate the convergence of the production simulation 76 | metad.plot_fes_convergence(stride=10, n_surfaces=5) 77 | 78 | # Block averaging analysis method also generates a set of free energy 79 | # surfaces with different block sizes which can be used in plot_fes() 80 | # method. 81 | 82 | # To use block averaging analysis, an appropriate start time should be 83 | # used (most of the bias should be deposited before start time for block 84 | # averaging analysis to be trusted). 85 | metad.block_analysis(start_time=5) 86 | -------------------------------------------------------------------------------- /examples/metadynamics/h2/h2.xyz: -------------------------------------------------------------------------------- 1 | 2 2 | 3 | H 0 0 0 4 | H 0 0 1 -------------------------------------------------------------------------------- /examples/metadynamics/h2o/h2o.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 8 4 | 5 | if __name__ == '__main__': 6 | # Initialise the system 7 | h2o_system = mlt.System(mlt.Molecule('h2o.xyz'), box=None) 8 | 9 | # Generate a starting metadynamics configuration 10 | h2o_config = h2o_system.random_configuration() 11 | 12 | # Define CVs, can also attach walls to them. More complicated CVs (i.e. 13 | # not DISTANCE, ANGLE, or TORSION; e.g. PATH) can be defined using 14 | # PlumedCustomCV that requires a PLUMED-like input file containing the 15 | # definition of the CV 16 | cv1 = mlt.PlumedAverageCV(name='cv1', atom_groups=((0, 1), (0, 2))) 17 | cv1.attach_upper_wall(location=5, kappa=10000) 18 | 19 | cv2 = mlt.PlumedAverageCV(name='cv2', atom_groups=(1, 0, 2)) 20 | cv2.attach_upper_wall(location=3.14, kappa=10000) 21 | 22 | # Attach CVs to the metadynamics object 23 | metad = mlt.Metadynamics(cvs=(cv1, cv2)) 24 | 25 | # Train (or load) a machine learning potential 26 | ace = mlt.potentials.ACE(name='water', system=h2o_system) 27 | 28 | # Active learning (can be commented out if the potential is loaded) 29 | al_bias = mlt.PlumedBias(cvs=(cv1, cv2)) 30 | al_bias.initialise_for_metad_al(width=(0.05, 0.10), biasfactor=100) 31 | ace.al_train( 32 | method_name='xtb', 33 | temp=300, 34 | max_active_iters=50, 35 | min_active_iters=10, 36 | bias_start_iter=2, 37 | inherit_metad_bias=True, 38 | bias=al_bias, 39 | ) 40 | 41 | # Can run optional methods (estimate_width() and try_multiple_biafactors()) 42 | # to help choose appropriate metadynamics parameters (width and bias factor), 43 | width = metad.estimate_width(configurations=h2o_config, mlp=ace, plot=True) 44 | 45 | metad.try_multiple_biasfactors( 46 | configuration=h2o_config, 47 | mlp=ace, 48 | temp=300, 49 | interval=10, 50 | dt=1, 51 | width=width, 52 | biasfactors=(5, 10, 15), 53 | plotted_cvs=(cv1, cv2), 54 | ps=40, 55 | ) 56 | 57 | # Execute metadynamics production runs, 8 independent simulations are 58 | # performed in parallel 59 | metad.run_metadynamics( 60 | configuration=h2o_config, 61 | mlp=ace, 62 | temp=300, 63 | interval=10, 64 | dt=1, 65 | width=width, 66 | biasfactor=5, 67 | n_runs=8, 68 | restart=False, 69 | ps=40, 70 | ) 71 | 72 | # Plot the resulting free energy surface (FES), the same method can be used 73 | # to plot the FES from block analysis or FES from a previous simulation 74 | # using .npy file, for all the options see the documentation of plot_fes() 75 | metad.plot_fes() 76 | 77 | # Can run optional methods (plot_fes_convergence() and block_analysis()) 78 | # to estimate the convergence of the production simulation 79 | metad.plot_fes_convergence(stride=10, n_surfaces=5) 80 | 81 | # Block analysis method also generates a set of free energy surfaces with 82 | # different block sizes which can be used in plot_fes() method 83 | 84 | # To use block averaging analysis, an appropriate start time should be 85 | # used (most of the bias should be deposited before start time for block 86 | # averaging analysis to be trusted). 87 | metad.block_analysis(start_time=10) 88 | -------------------------------------------------------------------------------- /examples/metadynamics/h2o/h2o.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -1.85501 -0.92269 0.00271 4 | H -0.86568 -0.96530 -0.00191 5 | H -2.14438 -1.86421 -0.09929 6 | -------------------------------------------------------------------------------- /examples/methane.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | # Set up the system of a methane molecule without any periodic boundaries 9 | system = mlt.System(mlt.Molecule('methane.xyz'), box=None) 10 | 11 | # Initialise a Gaussian Approximation Potential for this system 12 | gap = mlt.potentials.GAP('methane', system=system) 13 | 14 | # and train using active learning at 1000 K 15 | gap.al_train(method_name='orca', temp=1000) 16 | 17 | # Run some dynamics with the potential 18 | trajectory = mlt.md.run_mlp_md( 19 | configuration=system.random_configuration(), 20 | mlp=gap, 21 | fs=200, 22 | temp=300, 23 | dt=0.5, 24 | interval=10, 25 | ) 26 | 27 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 28 | trajectory.compare(gap, 'orca') 29 | -------------------------------------------------------------------------------- /examples/methane.xyz: -------------------------------------------------------------------------------- 1 | 5 2 | 3 | C 0.00090 0.00410 -0.02020 4 | H -0.65770 -0.84810 -0.32140 5 | H -0.45850 0.97520 -0.30610 6 | H 0.08530 -0.02530 1.08040 7 | H 1.03000 -0.10580 -0.43270 8 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/ace_parity/da_data.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/examples/paper_examples/r1_fig2/ace_parity/da_data.npz -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/ace_parity/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 12 4 | mlt.Config.orca_keywords = ['MP2', 'def2-TZVP', 'NOFROZENCORE'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts_pbe0.xyz'), box=None) 9 | ace = mlt.potentials.ACE('da', system=system) 10 | 11 | ace.set_atomic_energies('orca') 12 | ace.train(mlt.ConfigurationSet('da_data.npz')) 13 | 14 | # Run some dynamics with the potential 15 | trajectory = mlt.md.run_mlp_md( 16 | configuration=system.random_configuration(), 17 | mlp=ace, 18 | fs=500, 19 | temp=300, 20 | dt=0.5, 21 | interval=10, 22 | ) 23 | 24 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 25 | trajectory.compare(ace, 'orca') 26 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/ace_parity/ts_pbe0.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/ace/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts_pbe0.xyz'), box=None) 9 | 10 | ace = mlt.potentials.ACE('da', system=system) 11 | 12 | ace.al_train( 13 | method_name='orca', temp=500, max_active_time=500, fix_init_config=True 14 | ) 15 | # Run some dynamics with the potential 16 | trajectory = mlt.md.run_mlp_md( 17 | configuration=system.random_configuration(), 18 | mlp=ace, 19 | fs=500, 20 | temp=300, 21 | dt=0.5, 22 | interval=10, 23 | ) 24 | 25 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 26 | trajectory.compare(ace, 'orca') 27 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/ace/ts_pbe0.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/ace_uplift/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 12 4 | mlt.Config.orca_keywords = ['MP2', 'def2-TZVP', 'NOFROZENCORE', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts_pbe0.xyz'), box=None) 9 | ace = mlt.potentials.ACE('da', system=system) 10 | 11 | dft_al_data = mlt.ConfigurationSet() 12 | dft_al_data.load_xyz('da_data.xyz', charge=0, mult=1) 13 | dft_al_data.save('da_data.npz') 14 | 15 | dft_al_data.single_point(method_name='orca') 16 | dft_al_data.save('da_data.npz') 17 | 18 | ace.train(dft_al_data) 19 | 20 | # Run some dynamics with the potential 21 | trajectory = mlt.md.run_mlp_md( 22 | configuration=system.random_configuration(), 23 | mlp=ace, 24 | fs=500, 25 | temp=300, 26 | dt=0.5, 27 | interval=10, 28 | ) 29 | 30 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 31 | trajectory.compare(ace, 'orca') 32 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/ace_uplift/ts_pbe0.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/ace_uplift/uplift/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 12 4 | mlt.Config.orca_keywords = ['MP2', 'def2-TZVP', 'NOFROZENCORE'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts_pbe0.xyz'), box=None) 9 | ace = mlt.potentials.ACE('da', system=system) 10 | 11 | # dft_al_data = mlt.ConfigurationSet() 12 | # dft_al_data.load_xyz('da_data.xyz', charge=0, mult=1) 13 | # dft_al_data.save('da_data.npz') 14 | 15 | # dft_al_data.single_point(method_name='orca') 16 | # dft_al_data.save('da_data.npz') 17 | 18 | ace.set_atomic_energies('orca') 19 | ace.train(mlt.ConfigurationSet('da_data.npz')) 20 | 21 | # Run some dynamics with the potential 22 | trajectory = mlt.md.run_mlp_md( 23 | configuration=system.random_configuration(), 24 | mlp=ace, 25 | fs=500, 26 | temp=300, 27 | dt=0.5, 28 | interval=10, 29 | ) 30 | 31 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 32 | trajectory.compare(ace, 'orca') 33 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/ace_uplift/uplift/ts_pbe0.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/gap/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts_pbe0.xyz'), box=None) 9 | 10 | gap = mlt.potentials.GAP('da', system=system) 11 | 12 | gap.al_train( 13 | method_name='orca', temp=500, max_active_time=500, fix_init_config=True 14 | ) 15 | 16 | # Run some dynamics with the potential 17 | trajectory = mlt.md.run_mlp_md( 18 | configuration=system.random_configuration(), 19 | mlp=gap, 20 | fs=500, 21 | temp=300, 22 | dt=0.5, 23 | interval=10, 24 | ) 25 | 26 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 27 | trajectory.compare(gap, 'orca') 28 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/gap/ts_pbe0.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/nequip/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts_pbe0.xyz'), box=None) 9 | 10 | mlp = mlt.potentials.NeQUIP('da', system=system) 11 | 12 | mlp.al_train( 13 | method_name='orca', temp=500, max_active_time=500, fix_init_config=True 14 | ) 15 | 16 | # Run some dynamics with the potential 17 | trajectory = mlt.md.run_mlp_md( 18 | configuration=system.random_configuration(), 19 | mlp=mlp, 20 | fs=500, 21 | temp=300, 22 | dt=0.5, 23 | interval=10, 24 | ) 25 | 26 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 27 | trajectory.compare(mlp, 'orca') 28 | -------------------------------------------------------------------------------- /examples/paper_examples/r1_fig2/mlp_comparison/nequip/ts_pbe0.xyz: -------------------------------------------------------------------------------- 1 | 16 2 | Generated by autodE on: 2021-09-22. E = -234.090789 Ha 3 | C -0.37459 1.43042 0.68642 4 | C 0.44233 1.49793 -0.46180 5 | C 0.35630 0.56980 -1.47315 6 | C 0.81988 -1.37617 -0.34703 7 | C 0.02497 -1.44475 0.77516 8 | C -1.30576 0.43364 0.86149 9 | H -0.11127 2.06308 1.54026 10 | H 1.29973 2.17853 -0.44130 11 | H 1.08004 0.57759 -2.29267 12 | H -0.58707 0.06748 -1.69132 13 | H 0.55881 -1.94721 -1.24139 14 | H 1.86768 -1.08352 -0.26328 15 | H -0.87228 -2.06847 0.77719 16 | H 0.43438 -1.20285 1.75725 17 | H -1.84071 0.33771 1.81019 18 | H -1.79245 -0.03320 0.00410 19 | -------------------------------------------------------------------------------- /examples/paper_examples/r2_fig3/train_plus_ts7.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts7.xyz'), box=None) 9 | 10 | ace = mlt.potentials.ACE('da', system=system) 11 | 12 | ace.training_data = mlt.ConfigurationSet('da_al_ts5.npz') 13 | 14 | ace.al_train( 15 | method_name='orca', 16 | temp=500, 17 | max_active_time=1000, 18 | fix_init_config=True, 19 | ) 20 | # Run some dynamics with the potential 21 | trajectory = mlt.md.run_mlp_md( 22 | configuration=system.random_configuration(), 23 | mlp=ace, 24 | fs=500, 25 | temp=300, 26 | dt=0.5, 27 | interval=10, 28 | ) 29 | 30 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 31 | trajectory.compare(ace, 'orca') 32 | -------------------------------------------------------------------------------- /examples/paper_examples/r2_fig3/train_ts5.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('ts5.xyz'), box=None) 9 | 10 | ace = mlt.potentials.ACE('da', system=system) 11 | 12 | ace.al_train( 13 | method_name='orca', 14 | temp=500, 15 | max_active_time=1000, 16 | fix_init_config=True, 17 | ) 18 | # Run some dynamics with the potential 19 | trajectory = mlt.md.run_mlp_md( 20 | configuration=system.random_configuration(), 21 | mlp=ace, 22 | fs=500, 23 | temp=300, 24 | dt=0.5, 25 | interval=10, 26 | ) 27 | 28 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 29 | trajectory.compare(ace, 'orca') 30 | -------------------------------------------------------------------------------- /examples/paper_examples/r2_fig3/ts5.xyz: -------------------------------------------------------------------------------- 1 | 29 2 | Coordinates from ORCA-job opt_ts 3 | C -0.07318435613270 1.63238036263462 -0.30890124024240 4 | C -0.49802885135258 1.18069407890142 1.05072389013113 5 | C -0.58235946684242 1.04812449905354 -1.53540030559421 6 | C -1.74728059722882 0.58691790517841 1.37878684836431 7 | C -1.57123215208161 0.10927628928370 -1.71321080261544 8 | C -2.55204668421086 -0.22136395485714 0.58813028232616 9 | C -2.41288893763275 -0.51315181238120 -0.77077473334153 10 | H -2.06662351848465 0.70749742933758 2.41969863174027 11 | H -3.40369820538399 -0.69025065381621 1.09203372297354 12 | H -3.13112676296202 -1.22928494808716 -1.18176517349094 13 | H -1.73528592956229 -0.20355014145119 -2.75020178148683 14 | H -0.09647749858896 1.46427449231030 -2.42168374341746 15 | H -0.16970676525982 1.94232725221604 1.76384086089953 16 | O 0.80763334786327 2.48592457759376 -0.35511639237844 17 | C 0.32304063388543 -1.52128002511412 1.46059062488681 18 | C 0.90254008773739 -0.12741058717666 1.60001289327532 19 | C 0.17064390675741 -2.13134849405878 0.10658868822561 20 | C 2.05873375069482 0.32245668036489 0.93424781445299 21 | C 0.83074460664421 -1.83539221862289 -1.03683351474511 22 | C 2.45586979152393 0.01404568662668 -0.37672144538273 23 | C 1.85391533527349 -0.85701065353762 -1.26459489722096 24 | H 2.61839053651093 1.12549235381286 1.42191932063586 25 | H 3.29457620994892 0.59658315419351 -0.76751302369108 26 | H 2.27557170445342 -0.86533173089435 -2.27471200538298 27 | H 0.56179150011091 -2.42932467680138 -1.91623979493824 28 | H -0.53434339209232 -2.96877680428790 0.06315470629682 29 | H 0.87876272443818 0.17932301691352 2.65188646280425 30 | H 0.91450817501590 -2.20927751650768 2.09727793763370 31 | H -0.67662119304241 -1.50504556082655 1.93553316928203 32 | -------------------------------------------------------------------------------- /examples/paper_examples/r2_fig3/ts7.xyz: -------------------------------------------------------------------------------- 1 | 29 2 | 3 | C -0.047482 -1.510434 0.091366 4 | C -0.366278 -0.852531 -1.230116 5 | C -0.659631 -1.283006 1.321473 6 | C -1.680248 -0.243938 -1.516233 7 | C -1.769871 -0.468124 1.608360 8 | C -2.573922 0.276427 -0.657442 9 | C -2.559569 0.266915 0.773895 10 | H -1.925652 -0.177619 -2.572239 11 | H -3.448398 0.743439 -1.100656 12 | H -3.351170 0.837984 1.246068 13 | H -2.010017 -0.390593 2.664287 14 | H -0.204077 -1.821742 2.143562 15 | H -0.189389 -1.643084 -1.960640 16 | O 1.017726 -2.172527 0.000493 17 | C 0.562821 1.625043 -1.265973 18 | C 0.894468 0.146541 -1.491008 19 | C 0.129227 2.055200 0.097003 20 | C 2.054970 -0.393336 -0.753676 21 | C 0.652984 1.642975 1.254820 22 | C 2.444944 -0.032116 0.538871 23 | C 1.739443 0.722089 1.442635 24 | H 2.731399 -1.031192 -1.304754 25 | H 3.313433 -0.552039 0.924303 26 | H 2.121438 0.691977 2.458267 27 | H 0.239424 2.057644 2.166698 28 | H -0.657867 2.799098 0.137208 29 | H 1.102336 0.047285 -2.557228 30 | H 1.434005 2.225119 -1.558453 31 | H -0.228415 1.883708 -1.974195 32 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig4/cis_endo_TS_M06.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.49796381942179 4.25845136207721 -0.68357761211104 4 | C -3.33047230234756 3.18830573996282 -0.01734727771958 5 | C -2.43448902806707 2.19675837763489 0.40277910832723 6 | H -4.24611381475560 3.41707571342913 0.51223927277008 7 | C -1.23129720600660 2.34595884353595 -0.29216204601679 8 | H -2.69441870095277 1.35738816155015 1.03139096100724 9 | C -1.32750639784173 3.44223947699415 -1.13004369967434 10 | H -0.42090321088732 1.63144829127943 -0.28510312986284 11 | H -0.54007562898420 3.82119227292919 -1.76678410961208 12 | H -2.14681877421269 4.94334999173720 0.10014215092177 13 | H -3.00012018452296 4.84498031819239 -1.44852314774823 14 | C -2.96340192768997 2.36045647761320 -2.61244867440595 15 | C -4.03264592819788 2.41009726725642 -1.72156578738010 16 | H -4.78419245819584 3.17818990482845 -1.86714556298308 17 | H -4.40484655403546 1.46473275433517 -1.34868562576698 18 | C -2.26719848925262 1.08649435566726 -2.81471795526303 19 | H -2.79991548876295 3.14929490755143 -3.33671042020454 20 | O -2.35901046313074 0.15920936955784 -2.02349218828707 21 | C -1.46884278006962 0.96212459717699 -4.10399988683850 22 | H -1.03899811432714 1.91765413325036 -4.40931923166318 23 | H -2.16380409593850 0.64419339304567 -4.88356800794660 24 | H -0.68857879669907 0.21134957219474 -4.00849700524234 25 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig4/cis_endo_TS_PBE0.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.45625042994264 4.22697136718373 -0.69050566209957 4 | C -3.28702195379458 3.16527993919028 -0.02209098677282 5 | C -2.41028766314595 2.14322126716790 0.34022614745311 6 | H -4.18631817798479 3.39651295844350 0.53629598217147 7 | C -1.20817318676536 2.29480857602016 -0.36982113539345 8 | H -2.66413749952356 1.29424573410959 0.95978388172974 9 | C -1.28146913102924 3.42909805269927 -1.13984450034102 10 | H -0.40867019996620 1.56728893158943 -0.38835776572309 11 | H -0.50683361420779 3.80598439935262 -1.79369703691900 12 | H -2.11400085795616 4.92541367601641 0.08909189132240 13 | H -2.95734970838890 4.81263532467316 -1.45923411982257 14 | C -3.10205484705813 2.37368598258547 -2.68346342635020 15 | C -4.11637859827911 2.44256568361059 -1.74418593887422 16 | H -4.85151124593427 3.23644849500096 -1.82006001153415 17 | H -4.47736922293900 1.50457689648386 -1.33925710572378 18 | C -2.36389642614390 1.13176739960370 -2.83295879682348 19 | H -2.91472881437345 3.18240648333903 -3.37940748993174 20 | O -2.50989176905760 0.20606095908980 -2.04760734145413 21 | C -1.42166214759852 1.00292166731278 -4.00365141692310 22 | H -1.03881265683699 1.96596828196554 -4.34659501197343 23 | H -1.96765811314529 0.54168812578948 -4.83288296604129 24 | H -0.59713790022863 0.34139508057279 -3.73891706567561 25 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig4/cis_endo_TS_wB97X.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job cis_endo_TS 3 | C -2.45916198039135 4.23625428848756 -0.68443051791989 4 | C -3.30724428191780 3.17306340593524 -0.03018689926151 5 | C -2.42927318853393 2.16086017997894 0.36504416110616 6 | H -4.20494170131777 3.41404081820002 0.52586283684424 7 | C -1.22194515692719 2.30149810087639 -0.32995989049500 8 | H -2.69270464671053 1.32382434980323 0.99576109656134 9 | C -1.29340435330047 3.41708926722411 -1.13078021114416 10 | H -0.42080985262612 1.57667060056315 -0.32571937019656 11 | H -0.51017444669613 3.78700202675678 -1.77763363864295 12 | H -2.10854843422867 4.92168863286099 0.09867158195766 13 | H -2.95081440146019 4.82885118621772 -1.45237117780753 14 | C -3.05612207793502 2.38006155156123 -2.65676136632803 15 | C -4.08125544697042 2.43857125270586 -1.72115923150562 16 | H -4.83203766328172 3.21342545403628 -1.82683441393420 17 | H -4.45152115382227 1.49502950696877 -1.33882022192111 18 | C -2.34247097794300 1.12627760809051 -2.84773811615322 19 | H -2.88820421631018 3.18135181899255 -3.36459002907883 20 | O -2.48034413047616 0.18915314829056 -2.07742438281692 21 | C -1.43642727309939 0.99838303280899 -4.05243994826895 22 | H -1.09072178922283 1.96104869022091 -4.43233743103017 23 | H -1.99912254844589 0.49715025911794 -4.84435041260005 24 | H -0.58436444268303 0.36965010210232 -3.79894229306464 25 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig4/endo_ace_M06.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['M062X', 'RIJCOSX', 'def2/J', 'def2-SVP', 'EnGrad'] 5 | 6 | if __name__ == '__main__': 7 | system = mlt.System( 8 | mlt.Molecule('cis_endo_TS_M06.xyz', charge=0, mult=1), box=None 9 | ) 10 | 11 | ace = mlt.potentials.ACE('endo_ace', system=system) 12 | 13 | ace.al_train( 14 | method_name='orca', 15 | temp=500, 16 | max_active_time=1000, 17 | fix_init_config=True, 18 | ) 19 | 20 | # Run some dynamics with the potential 21 | trajectory = mlt.md.run_mlp_md( 22 | configuration=system.configuration, 23 | mlp=ace, 24 | fs=200, 25 | temp=300, 26 | dt=0.5, 27 | interval=10, 28 | ) 29 | 30 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 31 | trajectory.compare(ace, 'orca') 32 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig4/endo_ace_PBE0.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = ['PBE0', 'def2-SVP', 'EnGrad'] 5 | 6 | if __name__ == '__main__': 7 | system = mlt.System( 8 | mlt.Molecule('cis_endo_TS_PBE0.xyz', charge=0, mult=1), box=None 9 | ) 10 | 11 | ace = mlt.potentials.ACE('endo_ace', system=system) 12 | 13 | ace.al_train( 14 | method_name='orca', 15 | temp=500, 16 | max_active_time=1000, 17 | fix_init_config=True, 18 | ) 19 | 20 | # Run some dynamics with the potential 21 | trajectory = mlt.md.run_mlp_md( 22 | configuration=system.configuration, 23 | mlp=ace, 24 | fs=200, 25 | temp=300, 26 | dt=0.5, 27 | interval=10, 28 | ) 29 | 30 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 31 | trajectory.compare(ace, 'orca') 32 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig4/endo_ace_wB97X.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | mlt.Config.orca_keywords = [ 5 | 'wB97X-D3', 6 | 'RIJCOSX', 7 | 'def2/J', 8 | 'def2-SVP', 9 | 'EnGrad', 10 | ] 11 | 12 | if __name__ == '__main__': 13 | system = mlt.System( 14 | mlt.Molecule('cis_endo_TS_wB97X.xyz', charge=0, mult=1), box=None 15 | ) 16 | 17 | ace = mlt.potentials.ACE('endo_ace', system=system) 18 | 19 | ace.al_train( 20 | method_name='orca', 21 | temp=500, 22 | max_active_time=1000, 23 | fix_init_config=True, 24 | ) 25 | 26 | # Run some dynamics with the potential 27 | trajectory = mlt.md.run_mlp_md( 28 | configuration=system.configuration, 29 | mlp=ace, 30 | fs=200, 31 | temp=300, 32 | dt=0.5, 33 | interval=10, 34 | ) 35 | 36 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 37 | trajectory.compare(ace, 'orca') 38 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig5/train_endo.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | import autode as ade 3 | 4 | mlt.Config.n_cores = 10 5 | mlt.Config.orca_keywords = ade.Config.ORCA.keywords.grad 6 | 7 | 8 | if __name__ == '__main__': 9 | system = mlt.System(mlt.Molecule('ts_endo.xyz'), box=None) 10 | 11 | ace = mlt.potentials.ACE('da', system=system) 12 | 13 | ace.al_train( 14 | method_name='orca', temp=500, max_active_time=500, fix_init_config=True 15 | ) 16 | 17 | # Run some dynamics with the potential 18 | trajectory = mlt.md.run_mlp_md( 19 | configuration=system.configuration, 20 | mlp=ace, 21 | fs=500, 22 | temp=300, 23 | dt=0.5, 24 | interval=10, 25 | ) 26 | 27 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 28 | trajectory.compare(ace, 'orca') 29 | -------------------------------------------------------------------------------- /examples/paper_examples/r3_fig5/ts_endo.xyz: -------------------------------------------------------------------------------- 1 | 22 2 | Coordinates from ORCA-job TS_VP4HBa_ll_ad_2-7_3-4_optts_orca 3 | C 2.59461966299937 1.08481991931654 0.30322019269454 4 | C 1.51213860390981 0.57922739007157 -0.62361967523965 5 | C 0.95882520364468 -0.75225120965071 -0.38581474966688 6 | C -0.00000244801284 -1.22982570870931 -1.26814329962846 7 | C -1.84397121169085 -0.69668595819569 -0.43803328690596 8 | C -1.80809682617079 0.69906960141295 -0.52499184499209 9 | C -1.07525274232074 1.20778382163367 0.56811569617808 10 | C -0.67789152535715 0.15917337884417 1.37113804632906 11 | C -1.45177176208946 -1.05407509868428 0.97298737251883 12 | O 1.07949569576168 1.28958670354167 -1.51916203433648 13 | H 3.28540772127302 1.72659382119108 -0.26084616638799 14 | H 2.12774899278359 1.70558256257566 1.08511471830631 15 | H 3.14953095260079 0.27553804192330 0.79933027582091 16 | H 1.38498822984424 -1.38133052179147 0.39828403302150 17 | H -0.11448608641382 -0.68583581584856 -2.20779188344872 18 | H -0.23216058030923 -2.29751788089608 -1.29528267343369 19 | H -2.51223527733119 -1.31777193042378 -1.03911303606048 20 | H -2.17710150568475 1.29122207022919 -1.36279261952707 21 | H -0.77423749029548 2.24907116572024 0.69064284621928 22 | H -0.10615258278262 0.24979690558692 2.29603818405984 23 | H -0.94079146630733 -2.01653621410961 1.10858824970769 24 | H -2.37840362805092 -1.08553511373749 1.58073170477144 25 | -------------------------------------------------------------------------------- /examples/paper_examples/r4_tab1/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | import autode as ade 3 | 4 | mlt.Config.n_cores = 10 5 | mlt.Config.orca_keywords = ade.Config.ORCA.keywords.grad 6 | 7 | 8 | if __name__ == '__main__': 9 | system = mlt.System(mlt.Molecule('ts.xyz'), box=None) 10 | 11 | ace = mlt.potentials.ACE('da', system=system) 12 | 13 | ace.al_train_then_bias( 14 | method_name='orca', 15 | coordinate=mlt.AverageDistance((0, 1), (2, 3)), 16 | max_coordinate=3.5, 17 | selection_method=mlt.training.selection.AbsDiffE(0.043), 18 | temp=500, 19 | max_active_time=500, 20 | fix_init_config=True, 21 | ) 22 | 23 | # Run some dynamics with the potential 24 | trajectory = mlt.md.run_mlp_md( 25 | configuration=system.configuration, 26 | mlp=ace, 27 | fs=500, 28 | temp=300, 29 | dt=0.5, 30 | interval=10, 31 | ) 32 | 33 | # and compare, plotting a parity diagram and E_true, ∆E and ∆F 34 | trajectory.compare(ace, 'orca') 35 | -------------------------------------------------------------------------------- /examples/paper_examples/r4_tab1/ts.xyz: -------------------------------------------------------------------------------- 1 | 15 2 | Coordinates from ORCA-job TS__C-Awm_ll_ad_0-1_2-3_optts_orca 3 | C 0.61589474751140 1.08306598896479 0.15194994252373 4 | C 1.29791306713923 -0.65788031620389 -1.14832979092297 5 | C 0.49949588248752 -1.56284094938019 -0.86607715143155 6 | C -0.87482130956703 -0.59530532447924 0.67844087436788 7 | C 0.33554284445827 0.08945279075415 1.23997752181521 8 | C -1.49769047426083 0.30507018447685 -0.17718636399258 9 | C -0.58661683790721 1.33151940102296 -0.49874893532615 10 | H 1.47008627913733 1.76373271748136 0.16278714159749 11 | H 2.13338438443162 -0.17584701391435 -1.62910962919141 12 | H 0.04374977963227 -2.53904557771512 -0.88867999533498 13 | H -1.37179318246299 -1.43706108064055 1.16594756559220 14 | H 1.16876025872841 -0.55779556718096 1.53619053973375 15 | H 0.00783812552535 0.66050768426639 2.13374658403232 16 | H -2.48373495551788 0.17406161584332 -0.62589904714441 17 | H -0.75786858933546 2.11842544670448 -1.23509927631851 18 | -------------------------------------------------------------------------------- /examples/umbrella/sn2.xyz: -------------------------------------------------------------------------------- 1 | 6 2 | 3 | C 4.6294000000 5.1250800000 5.6408500000 4 | Cl 6.6275700000 6.2375900000 4.9126900000 5 | H 4.4464600000 4.9197500000 4.5561400000 6 | H 5.3184900000 4.4268200000 6.1770700000 7 | H 4.3042600000 6.0871000000 6.0608900000 8 | Cl 2.5879500000 4.1441400000 6.2943500000 9 | -------------------------------------------------------------------------------- /examples/umbrella/train.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 10 4 | 5 | 6 | if __name__ == '__main__': 7 | system = mlt.System(mlt.Molecule('sn2.xyz', charge=-1), box=None) 8 | 9 | ace = mlt.potentials.ACE('sn2', system=system) 10 | ace.al_train(method_name='xtb', temp=500, fix_init_config=True) 11 | -------------------------------------------------------------------------------- /examples/umbrella/umbrella.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 4 4 | 5 | if __name__ == '__main__': 6 | # Define a reaction coordinate as R1 - R2 7 | umbrella = mlt.UmbrellaSampling( 8 | zeta_func=mlt.DifferenceDistance((0, 1), (0, 5)), kappa=20 9 | ) 10 | 11 | irc = mlt.ConfigurationSet() 12 | irc.load_xyz(filename='irc.xyz', charge=-1, mult=1) 13 | 14 | system = mlt.System(mlt.Molecule('sn2.xyz', charge=-1, mult=1), box=None) 15 | 16 | # Run umbrella sampling across the IRC using GAP MD 17 | umbrella.run_umbrella_sampling( 18 | irc, 19 | mlp=mlt.potentials.GAP('sn2', system=system), 20 | temp=300, 21 | interval=5, 22 | dt=0.5, 23 | n_windows=10, 24 | ps=1, 25 | ) 26 | 27 | # Use WHAM to calculate the free energy 28 | umbrella.wham() 29 | -------------------------------------------------------------------------------- /examples/water.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | from mlptrain.training.selection import AtomicEnvSimilarity 3 | from mlptrain.descriptor import SoapDescriptor 4 | 5 | mlt.Config.n_cores = 10 6 | 7 | if __name__ == '__main__': 8 | system = mlt.System(mlt.Molecule('water.xyz'), box=None) 9 | 10 | ace = mlt.potentials.ACE('water', system=system) 11 | 12 | descriptor = SoapDescriptor(average='outer', r_cut=6.0, n_max=6, l_max=6) 13 | selector = AtomicEnvSimilarity(descriptor=descriptor, threshold=0.9995) 14 | ace.al_train(method_name='xtb', selection_method=selector, temp=500) 15 | 16 | # Run some dynamics with the potential 17 | trajectory = mlt.md.run_mlp_md( 18 | configuration=system.random_configuration(), 19 | mlp=ace, 20 | fs=200, 21 | temp=300, 22 | dt=0.5, 23 | interval=10, 24 | ) 25 | 26 | trajectory.save(filename='water_trajectory.xyz') 27 | -------------------------------------------------------------------------------- /examples/water.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -1.85501 -0.92269 0.00271 4 | H -0.86568 -0.96530 -0.00191 5 | H -2.14438 -1.86421 -0.09929 6 | -------------------------------------------------------------------------------- /examples/water_openmm.py: -------------------------------------------------------------------------------- 1 | import mlptrain as mlt 2 | 3 | mlt.Config.n_cores = 1 4 | 5 | 6 | if __name__ == '__main__': 7 | system = mlt.System(mlt.Molecule('water.xyz'), box=None) 8 | 9 | mace = mlt.potentials.MACE('water', system=system) 10 | 11 | mace.al_train( 12 | method_name='xtb', 13 | temp=500, 14 | max_active_iters=2, 15 | max_active_time=50, 16 | n_configs_iter=3, 17 | md_program='OpenMM', 18 | ) 19 | 20 | # Run some dynamics with the potential 21 | trajectory = mlt.md_openmm.run_mlp_md_openmm( 22 | configuration=system.random_configuration(), 23 | mlp=mace, 24 | fs=2000, 25 | temp=300, 26 | dt=0.5, 27 | interval=100, 28 | ) 29 | 30 | trajectory.save(filename='water_trajectory.xyz') 31 | -------------------------------------------------------------------------------- /examples/water_openmm_colab.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "id": "98up6lvBEmaE" 7 | }, 8 | "source": [ 9 | "# MLP-Train Water with OpenMM on Colab\n", 10 | "\n", 11 | "You can run this notebook in Google Colab: [![Open On Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/sef43/mlp-train/blob/main/examples/water_openmm_colab.ipynb)\n", 12 | "\n", 13 | "MLP-Train basic example that runs on Colab and uses OpenMM as the MD engine." 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": { 20 | "colab": { 21 | "base_uri": "https://localhost:8080/", 22 | "height": 1000 23 | }, 24 | "id": "Cb_3edQEEyxx", 25 | "outputId": "b02b5b81-db06-49e1-81d2-780f0e72079b" 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "# Installation on Colab\n", 30 | "!pip install -q condacolab\n", 31 | "import condacolab\n", 32 | "\n", 33 | "condacolab.install_mambaforge()\n", 34 | "# https://github.com/openmm/openmm-torch/issues/88\n", 35 | "%env CONDA_OVERRIDE_CUDA=12.0\n", 36 | "!mamba install -c conda-forge openmm-torch=1.1 pytorch=2.0 xtb cython rdkit\n", 37 | "\n", 38 | "# Only have python 3.10 available on Colab so cannot install autodE from\n", 39 | "# conda-forge as there are no py3.10 builds for autode=v1.1.*\n", 40 | "!git clone --branch v1.1.3 https://github.com/duartegroup/autodE.git\n", 41 | "!cd autodE && python setup.py install\n", 42 | "!cd ..\n", 43 | "!pip install ./autodE\n", 44 | "!pip install git+https://github.com/rosswhitfield/ase.git\n", 45 | "!pip install git+https://github.com/ACEsuit/mace.git\n", 46 | "!pip install git+https://github.com/sef43/openmm-ml@mace\n", 47 | "!pip install git+https://github.com/sef43/mlp-train" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": { 54 | "colab": { 55 | "base_uri": "https://localhost:8080/" 56 | }, 57 | "id": "jInwylB5aU3x", 58 | "outputId": "efa7baaf-5ee2-469a-e131-a2616dde5890" 59 | }, 60 | "outputs": [], 61 | "source": [ 62 | "# Get the example input\n", 63 | "!wget https://raw.githubusercontent.com/sef43/mlp-train/main/examples/water.xyz" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": { 70 | "colab": { 71 | "base_uri": "https://localhost:8080/" 72 | }, 73 | "id": "rdjXObniFynd", 74 | "outputId": "1ae4d16c-f2db-4f23-d42c-7115091fc3ad" 75 | }, 76 | "outputs": [], 77 | "source": [ 78 | "import mlptrain as mlt\n", 79 | "\n", 80 | "mlt.Config.n_cores = 1\n", 81 | "\n", 82 | "system = mlt.System(mlt.Molecule('water.xyz'), box=None)\n", 83 | "\n", 84 | "mace = mlt.potentials.MACE('water', system=system)\n", 85 | "\n", 86 | "mace.al_train(\n", 87 | " method_name='xtb',\n", 88 | " temp=500,\n", 89 | " max_active_iters=2,\n", 90 | " max_active_time=50,\n", 91 | " n_configs_iter=3,\n", 92 | " md_program='OpenMM',\n", 93 | ")\n", 94 | "\n", 95 | "# Run some dynamics with the potential\n", 96 | "trajectory = mlt.md_openmm.run_mlp_md_openmm(\n", 97 | " configuration=system.random_configuration(),\n", 98 | " mlp=mace,\n", 99 | " fs=2000,\n", 100 | " temp=300,\n", 101 | " dt=0.5,\n", 102 | " interval=100,\n", 103 | ")\n", 104 | "\n", 105 | "trajectory.save(filename='water_trajectory.xyz')" 106 | ] 107 | } 108 | ], 109 | "metadata": { 110 | "colab": { 111 | "provenance": [] 112 | }, 113 | "kernelspec": { 114 | "display_name": "Python 3", 115 | "name": "python3" 116 | }, 117 | "language_info": { 118 | "name": "python" 119 | } 120 | }, 121 | "nbformat": 4, 122 | "nbformat_minor": 0 123 | } 124 | -------------------------------------------------------------------------------- /install_ace.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Exit on error 3 | set -euo pipefail 4 | 5 | # Install mlptrain together with dependencies for ACE 6 | # NOTE: You need to install Julia >=1.6 before running this script! 7 | 8 | export CONDA_ENV_NAME=mlptrain-ace 9 | export CONDA_ENV_FILE=environment_ace.yml 10 | 11 | echo "* Looking for Julia executable *" 12 | if ! which julia; then 13 | echo "* ERROR: julia not found! *" 14 | echo "* Install Julia >= 1.6 first and add it to your PATH *" 15 | exit 1 16 | fi 17 | 18 | source create_conda_environment.sh 19 | 20 | echo "* Adding required registries and packages to Julia *" 21 | echo "using Pkg 22 | Pkg.Registry.add(\"General\") 23 | Pkg.Registry.add(RegistrySpec(url=\"https://github.com/JuliaMolSim/MolSim.git\")) 24 | Pkg.add(PackageSpec(name=\"JuLIP\", version=\"0.10.1\")) 25 | Pkg.add(PackageSpec(name=\"ACE\", version=\"0.8.4\")) 26 | Pkg.add(PackageSpec(name=\"IPFitting\", version=\"0.5.0\")) 27 | Pkg.add(\"IJulia\") 28 | Pkg.add(\"ASE\")" > add_julia_pkgs.jl 29 | julia add_julia_pkgs.jl 30 | 31 | # NOTE: `conda activate` does not work in scripts, need to use `conda run`, see: 32 | # https://stackoverflow.com/a/72395091 33 | echo "* Setting up Python-Julia integration *" 34 | $CONDA_EXE run -n ${CONDA_ENV_NAME} python -c "import julia; julia.install()" 35 | 36 | echo "* Pointing PyCall to the version of Python in the new env *" 37 | 38 | echo "ENV[\"PYTHON\"] = \"$(eval "which python")\" 39 | using Pkg 40 | Pkg.build(\"PyCall\")" > pycall.jl 41 | julia pycall.jl 42 | 43 | rm -f add_julia_pkgs.jl pycall.jl 44 | echo "* DONE! *" 45 | -------------------------------------------------------------------------------- /install_gap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Install mlptrain together with dependencies for GAP 5 | export CONDA_ENV_NAME=mlptrain-gap 6 | export CONDA_ENV_FILE=environment.yml 7 | 8 | source create_conda_environment.sh 9 | echo "* DONE! *" 10 | -------------------------------------------------------------------------------- /install_mace.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Install mlptrain together with dependencies for MACE 5 | export CONDA_ENV_NAME=mlptrain-mace 6 | export CONDA_ENV_FILE=environment_mace.yml 7 | 8 | source create_conda_environment.sh 9 | echo "* DONE! *" 10 | -------------------------------------------------------------------------------- /mlptrain/__init__.py: -------------------------------------------------------------------------------- 1 | from mlptrain.configurations import Configuration, ConfigurationSet, Trajectory 2 | from mlptrain.config import Config 3 | from mlptrain.molecule import Molecule 4 | from mlptrain.system import System 5 | from mlptrain.box import Box 6 | from mlptrain.sampling import md, md_openmm, UmbrellaSampling, Metadynamics 7 | from mlptrain.sampling import Bias, PlumedBias, PlumedCalculator 8 | from mlptrain.sampling.plumed import plot_cv_versus_time, plot_cv1_and_cv2 9 | from mlptrain.utils import convert_ase_time, convert_ase_energy 10 | from mlptrain import potentials 11 | from mlptrain import loss 12 | from mlptrain.training import selection 13 | from mlptrain.sampling.reaction_coord import ( 14 | AverageDistance, 15 | DifferenceDistance, 16 | ) 17 | from mlptrain.sampling.plumed import ( 18 | PlumedAverageCV, 19 | PlumedDifferenceCV, 20 | PlumedCNCV, 21 | PlumedCustomCV, 22 | ) 23 | 24 | __version__ = '1.0.0b0' 25 | 26 | __all__ = [ 27 | 'Configuration', 28 | 'ConfigurationSet', 29 | 'Trajectory', 30 | 'Config', 31 | 'Molecule', 32 | 'System', 33 | 'Box', 34 | 'Bias', 35 | 'PlumedBias', 36 | 'PlumedCalculator', 37 | 'UmbrellaSampling', 38 | 'Metadynamics', 39 | 'AverageDistance', 40 | 'DifferenceDistance', 41 | 'PlumedAverageCV', 42 | 'PlumedDifferenceCV', 43 | 'PlumedCNCV', 44 | 'PlumedCustomCV', 45 | 'plot_cv_versus_time', 46 | 'plot_cv1_and_cv2', 47 | 'convert_ase_time', 48 | 'convert_ase_energy', 49 | 'md', 50 | 'md_openmm', 51 | 'loss', 52 | 'selection', 53 | 'potentials', 54 | ] 55 | -------------------------------------------------------------------------------- /mlptrain/box.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from typing import Sequence 3 | 4 | 5 | class Box: 6 | def __init__(self, size: Sequence[float]): 7 | """ 8 | Periodic cuboidal box 9 | 10 | ----------------------------------------------------------------------- 11 | Arguments: 12 | size: Lattice vectors a, b, c defining the box size 13 | """ 14 | assert len(size) == 3 15 | self.size = np.array([float(k) for k in size]) 16 | 17 | @property 18 | def random_point(self) -> np.ndarray: 19 | """Get a random point inside the box""" 20 | return np.array([np.random.uniform(0.0, k) for k in self.size]) 21 | 22 | @property 23 | def volume(self) -> float: 24 | """Volume of this box""" 25 | return self.size[0] * self.size[1] * self.size[2] 26 | 27 | @property 28 | def has_zero_volume(self) -> bool: 29 | """Is this box essentially of zero size""" 30 | return self.volume < 1e-10 31 | 32 | @property 33 | def midpoint(self) -> np.ndarray: 34 | """Midpoint inside this box""" 35 | return self.size / 2.0 36 | 37 | def __eq__(self, other): 38 | """Equality of two boxes""" 39 | 40 | return ( 41 | isinstance(other, Box) 42 | and np.linalg.norm(other.size - self.size) < 1e-10 43 | ) 44 | -------------------------------------------------------------------------------- /mlptrain/config.py: -------------------------------------------------------------------------------- 1 | from autode.wrappers.keywords import GradientKeywords 2 | 3 | 4 | class _ConfigClass: 5 | """ 6 | MLP training configurations 7 | 8 | This class contains default parameters for electronic structure computations and training of available MLPs. 9 | Default settings for electronic structures is None to avoid accidentally running the wrong level of theory. 10 | The desired level can be specified by, e.g. 11 | ``` 12 | from mlptrain.config import Config 13 | 14 | Config.orca_keywords = ['PBE', 'def2-SVP', 'EnGrad'] 15 | Config.gaussian_keywords = ['PBEPBE', 'Def2SVP', 'Force(NoStep)', 'integral=ultrafinegrid'] 16 | ``` 17 | """ 18 | 19 | n_cores = 4 20 | _orca_keywords = None 21 | _gaussian_keywords = None 22 | 23 | # Default parameters for a GAP potential 24 | gap_default_params = { 25 | 'sigma_E': 10 ** (-4.0), # eV 26 | 'sigma_F': 10 ** (-2.0), 27 | } # eV Å-1 28 | 29 | # Default SOAP parameters 30 | gap_default_soap_params = { 31 | 'cutoff': 4.0, # Å 32 | 'n_sparse': 1000, 33 | 'l_max': 6, # n_max = 2 l_max 34 | 'sigma_at': 0.5, # Å 35 | } 36 | # ACE params 37 | ace_params = { 38 | 'N': 4, # maximum correlation order 39 | 'r_cut': 4.0, # outer cutoff of ACE 40 | 'deg_pair': 5, # Specify the pair potential 41 | 'r_cut_pair': 5.0, 42 | } 43 | 44 | # NeQUIP params 45 | nequip_params = {'cutoff': 4.0, 'train_fraction': 0.9} 46 | 47 | # MACE params 48 | 49 | try: 50 | import torch 51 | 52 | mace_device = 'cuda' if torch.cuda.is_available() else 'cpu' 53 | except ImportError: 54 | mace_device = 'cpu' 55 | 56 | mace_params = { 57 | 'valid_fraction': 0.1, 58 | 'config_type_weights': '{"Default":1.0}', 59 | 'model': 'MACE', 60 | 'loss': 'weighted', 61 | 'energy_weight': 1.0, 62 | 'forces_weight': 5.0, 63 | 'hidden_irreps': '128x0e + 128x1o', 64 | 'batch_size': 10, 65 | 'r_max': 5.0, 66 | 'correlation': 3, 67 | 'device': mace_device, 68 | 'calc_device': 'cpu', 69 | 'error_table': 'TotalMAE', 70 | 'swa': True, 71 | 'start_swa': 800, 72 | 'ema': False, 73 | 'ema_decay': 0.99, 74 | 'amsgrad': True, 75 | 'restart_latest': False, 76 | 'save_cpu': True, 77 | 'dtype': 'float32', 78 | } 79 | 80 | # --------------------- Internal properties --------------------------- 81 | 82 | @property 83 | def orca_keywords(self): 84 | return GradientKeywords(self._orca_keywords) 85 | 86 | @orca_keywords.setter 87 | def orca_keywords(self, value): 88 | """ORCA keywords must be gradient""" 89 | self._orca_keywords = value 90 | 91 | @property 92 | def gaussian_keywords(self): 93 | return GradientKeywords(self._gaussian_keywords) 94 | 95 | @gaussian_keywords.setter 96 | def gaussian_keywords(self, value): 97 | """Gaussian keywords must be gradient""" 98 | self._gaussian_keywords = value 99 | 100 | 101 | # Singleton instance of the configuration 102 | Config = _ConfigClass() 103 | -------------------------------------------------------------------------------- /mlptrain/configurations/__init__.py: -------------------------------------------------------------------------------- 1 | from mlptrain.configurations.configuration import Configuration 2 | from mlptrain.configurations.configuration_set import ConfigurationSet 3 | from mlptrain.configurations.trajectory import Trajectory 4 | 5 | __all__ = ['Configuration', 'ConfigurationSet', 'Trajectory'] 6 | -------------------------------------------------------------------------------- /mlptrain/configurations/calculate.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | import autode 3 | from typing import Tuple 4 | from mlptrain.log import logger 5 | from mlptrain.utils import work_in_tmp_dir 6 | from mlptrain.config import Config 7 | 8 | 9 | @work_in_tmp_dir() 10 | def run_autode( 11 | configuration: 'mlptrain.Configuration', method_name: str, n_cores: int = 1 12 | ) -> None: 13 | """ 14 | Run an autodE calculation 15 | 16 | --------------------------------------------------------------------------- 17 | Arguments: 18 | configuration: 19 | 20 | method_name: Name of the method. Case insensitive 21 | 22 | n_cores: Number of cores to use for the calculation 23 | """ 24 | from autode.species import Species 25 | from autode.calculations import Calculation 26 | from autode.exceptions import CouldNotGetProperty 27 | 28 | method, kwds = _method_and_keywords(method_name=method_name.lower()) 29 | logger.info(f'Running a {method_name} calculation at: {kwds}') 30 | 31 | calc = Calculation( 32 | name='tmp', 33 | molecule=Species( 34 | name='tmp', 35 | atoms=configuration.atoms, 36 | charge=configuration.charge, 37 | mult=configuration.mult, 38 | ), 39 | method=method, 40 | keywords=kwds, 41 | n_cores=n_cores, 42 | ) 43 | calc.run() 44 | 45 | try: 46 | configuration.forces.true = -calc.molecule.gradient.to('eV Å^-1') 47 | 48 | except CouldNotGetProperty: 49 | logger.error('Failed to set forces') 50 | 51 | energy = calc.molecule.energy 52 | if energy is None: 53 | logger.error('Failed to calculate the energy') 54 | if calc.output.exists: 55 | print(''.join(calc.output.file_lines[-50:])) 56 | 57 | return None 58 | 59 | configuration.energy.true = energy.to('eV') 60 | configuration.partial_charges = calc.molecule.partial_charges 61 | return None 62 | 63 | 64 | def _method_and_keywords( 65 | method_name: str, 66 | ) -> Tuple['autode.wrappers.Method', 'autode.wrappers.keywords.Keywords']: 67 | """Get the method and associated keywords to use in a QM calculation""" 68 | from autode.methods import ORCA, XTB, G16, G09 69 | 70 | if method_name == 'orca': 71 | method, kwds = ORCA(), _orca_keywords() 72 | 73 | elif method_name == 'g09' or method_name == 'g16': 74 | method = G09() if method_name == 'g09' else G16() 75 | kwds = _gaussian_keywords() 76 | 77 | elif method_name == 'xtb': 78 | method = XTB() 79 | kwds = method.keywords.grad 80 | 81 | else: 82 | raise ValueError(f'Unknown method {method_name}') 83 | 84 | return method, kwds 85 | 86 | 87 | def _orca_keywords() -> 'autode.wrappers.keywords.Keywords': 88 | """Keywords e.g. functional and basis set to use for an ORCA calculation""" 89 | 90 | if len(Config.orca_keywords) == 0: 91 | raise ValueError( 92 | 'For ORCA training GTConfig.orca_keywords must be' 93 | ' set. e.g.\nmlt.Config.orca_keywords ' 94 | "= ['PBE', 'def2-SVP', 'EnGrad'])" 95 | ) 96 | 97 | return Config.orca_keywords 98 | 99 | 100 | def _gaussian_keywords() -> 'autode.wrappers.keywords.Keywords': 101 | """Keywords e.g. functional and basis set to use for an Gaussian 102 | calculation, either Gaussian09 or Gaussian16""" 103 | 104 | if len(Config.gaussian_keywords) == 0: 105 | raise ValueError( 106 | 'To train with Gaussian QM calculations ' 107 | 'mlt.Config.gaussian_keywords must be set.' 108 | ) 109 | 110 | return Config.gaussian_keywords 111 | -------------------------------------------------------------------------------- /mlptrain/configurations/trajectory.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | from typing import Union 3 | 4 | from mlptrain.log import logger 5 | from mlptrain.configurations.configuration import Configuration 6 | from mlptrain.configurations.configuration_set import ConfigurationSet 7 | 8 | 9 | class Trajectory(ConfigurationSet): 10 | """Trajectory""" 11 | 12 | def __init__(self, *args: Union[Configuration, str]): 13 | super().__init__(*args, allow_duplicates=True) 14 | 15 | @property 16 | def t0(self) -> float: 17 | """Initial time of this trajectory 18 | 19 | ----------------------------------------------------------------------- 20 | Returns: 21 | (float): t_0 in fs 22 | """ 23 | return 0.0 if len(self) == 0 else self[0].time 24 | 25 | @t0.setter 26 | def t0(self, value: float): 27 | """Set the initial time for a trajectory""" 28 | 29 | for frame in self: 30 | if frame.time is None: 31 | logger.warning( 32 | 'Attempted to set the initial time but a ' 33 | f'time was note defined. Setting to {value}' 34 | ) 35 | frame.time = value 36 | 37 | else: 38 | frame.time += value 39 | 40 | return 41 | 42 | @property 43 | def final_frame(self) -> 'mlptrain.Configuration': 44 | """ 45 | Return the final frame from this trajectory 46 | 47 | ----------------------------------------------------------------------- 48 | Returns: 49 | (mlptrain.Configuration): Frame 50 | """ 51 | 52 | if len(self) == 0: 53 | raise ValueError('Had no final frame - no configurations present') 54 | 55 | return self[-1] 56 | -------------------------------------------------------------------------------- /mlptrain/descriptor/__init__.py: -------------------------------------------------------------------------------- 1 | from mlptrain.descriptor.soap_descriptor import SoapDescriptor 2 | 3 | __all__ = ['SoapDescriptor'] 4 | -------------------------------------------------------------------------------- /mlptrain/descriptor/_base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | import numpy as np 3 | from typing import Union 4 | from mlptrain.log import logger 5 | import mlptrain 6 | 7 | 8 | class Descriptor(ABC): 9 | """Abstract base class for molecular feature descriptors.""" 10 | 11 | def __init__(self, name: str): 12 | """ 13 | Initializes the descriptor representation. 14 | 15 | Arguments: 16 | name (str): Name of the descriptor. e.g., "ace_descriptor","soap_descriptor","mace_descriptor" 17 | """ 18 | self.name = str(name) 19 | logger.info(f'Initialized {self.name} descriptor.') 20 | 21 | @abstractmethod 22 | def compute_representation( 23 | self, 24 | configurations: Union[ 25 | mlptrain.Configuration, mlptrain.ConfigurationSet 26 | ], 27 | ) -> np.ndarray: 28 | """ 29 | Compute descriptor representation for a given molecular configuration. 30 | 31 | Arguments: 32 | configuration: A molecular structure (e.g., `mlptrain.Configuration`). 33 | Returns: 34 | np.ndarray: The computed descriptor representation as a vector/matrix. 35 | """ 36 | 37 | @abstractmethod 38 | def kernel_vector( 39 | self, configuration, configurations, zeta: int = 4 40 | ) -> np.ndarray: 41 | """Calculate the kernel matrix between a set of configurations where the kernel is: .. math:: 42 | 43 | K(p_a, p_b) = (p_a . p_b / (p_a.p_a x p_b.p.b)^1/2 )^ζ 44 | 45 | --------------------------------------------------------------------------- 46 | Arguments: 47 | configuration: 48 | 49 | configurations: 50 | 51 | zeta: Power to raise the kernel matrix to 52 | 53 | Returns: 54 | (np.ndarray): Vector, shape = len(configurations)""" 55 | -------------------------------------------------------------------------------- /mlptrain/descriptor/soap_descriptor.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import mlptrain 3 | from typing import Union, Optional, Sequence 4 | from dscribe.descriptors import SOAP 5 | from mlptrain.descriptor._base import Descriptor 6 | 7 | 8 | class SoapDescriptor(Descriptor): 9 | """SOAP Descriptor Representation.""" 10 | 11 | def __init__( 12 | self, 13 | elements: Optional[Sequence[str]] = None, 14 | r_cut: float = 5.0, 15 | n_max: int = 6, 16 | l_max: int = 6, 17 | average: Optional[str] = 'inner', 18 | ): 19 | """ 20 | SOAP Descriptor Representation. 21 | 22 | Initializes a SOAP descriptor for computing the Smooth Overlap of Atomic Positions (SOAP) representation. 23 | 24 | Arguments: 25 | elements (Optional[Sequence[str]]): Atomic species (e.g., `['H', 'O']`) for which the SOAP descriptor is computed. 26 | If `None`, elements will be inferred from input configurations. 27 | r_cut (float): Cutoff radius (Å) for considering atomic neighbors, defining the spatial range for SOAP calculations. 28 | n_max (int): Number of radial basis functions, affecting the resolution in the radial direction. 29 | l_max (int): Maximum degree of spherical harmonics, controlling the angular resolution. 30 | average (Optional[str]): Averaging mode for the SOAP descriptor: 31 | - `"inner"` (default): Averages SOAP vectors before computing the power spectrum. 32 | - `"outer"`: Computes the power spectrum for each atom, then averages. 33 | - `None`: No averaging, returns per-atom descriptors.""" 34 | 35 | super().__init__(name='SoapDescriptor') 36 | self.elements = elements 37 | self.r_cut = r_cut 38 | self.n_max = n_max 39 | self.l_max = l_max 40 | self.average = average 41 | 42 | # Initialize SOAP descriptor if elements are provided 43 | if self.elements: 44 | self.soap = SOAP( 45 | species=self.elements, 46 | r_cut=self.r_cut, 47 | n_max=self.n_max, 48 | l_max=self.l_max, 49 | average=self.average, 50 | ) 51 | else: 52 | self.soap = None # To be initialized dynamically 53 | 54 | def compute_representation( 55 | self, 56 | configurations: Union[ 57 | mlptrain.Configuration, mlptrain.ConfigurationSet 58 | ], 59 | ) -> np.ndarray: 60 | """Create a SOAP vector using dscribe (https://github.com/SINGROUP/dscribe) 61 | for a set of configurations 62 | 63 | soap_vector(config) -> [[v0, v1, ..]] 64 | 65 | soap_vector(config1, config2) -> [[v0, v1, ..], 66 | [u0, u1, ..]] 67 | 68 | soap_vector(configset) -> [[v0, v1, ..], ..] 69 | 70 | --------------------------------------------------------------------------- 71 | Arguments: 72 | args: Configurations to use 73 | 74 | 75 | Returns: 76 | np.ndarray: SOAP descriptor matrix of shape `(m, n)`, where: 77 | - `m` is the number of input configurations. 78 | - `n` is the descriptor dimensionality, dependent on `n_max` and `l_max`. 79 | """ 80 | 81 | if isinstance(configurations, mlptrain.Configuration): 82 | configurations = [ 83 | configurations 84 | ] # Convert to list if it's a single Configuration 85 | elif not isinstance(configurations, mlptrain.ConfigurationSet): 86 | raise ValueError( 87 | f'Unsupported configuration type: {type(configurations)}' 88 | ) 89 | 90 | # Dynamically set elements if not provided 91 | if self.soap is None: 92 | if not self.elements: 93 | self.elements = list( 94 | set(atom.label for c in configurations for atom in c.atoms) 95 | ) 96 | 97 | self.soap = SOAP( 98 | species=self.elements, 99 | r_cut=self.r_cut, 100 | n_max=self.n_max, 101 | l_max=self.l_max, 102 | average=self.average, 103 | ) 104 | 105 | soap_vec = self.soap.create( 106 | [conf.ase_atoms for conf in configurations] 107 | ) 108 | return soap_vec if soap_vec.ndim > 1 else soap_vec.reshape(1, -1) 109 | 110 | def kernel_vector( 111 | self, 112 | configuration: mlptrain.Configuration, 113 | configurations: mlptrain.ConfigurationSet, 114 | zeta: int = 4, 115 | ) -> np.ndarray: 116 | """Calculate the kernel matrix between a set of configurations where the 117 | kernel is: 118 | 119 | .. math:: 120 | 121 | K(p_a, p_b) = (p_a . p_b / (p_a.p_a x p_b.p_b)^1/2 )^ζ 122 | 123 | --------------------------------------------------------------------------- 124 | Arguments: 125 | configuration: 126 | 127 | configurations: 128 | 129 | zeta: Power to raise the kernel matrix to 130 | 131 | Returns: 132 | (np.ndarray): Vector, shape = len(configurations)""" 133 | 134 | v1 = self.compute_representation(configuration)[0] 135 | m1 = self.compute_representation(configurations) 136 | 137 | # Normalize vectors 138 | v1 /= np.linalg.norm(v1) 139 | m1 /= np.linalg.norm(m1, axis=1, keepdims=True) 140 | 141 | return np.power(np.dot(m1, v1), zeta) 142 | -------------------------------------------------------------------------------- /mlptrain/energy.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class Energy: 5 | """Energy in units of eV""" 6 | 7 | def __init__( 8 | self, 9 | predicted: Optional[float] = None, 10 | true: Optional[float] = None, 11 | bias: Optional[float] = None, 12 | inherited_bias: Optional[float] = None, 13 | ): 14 | """ 15 | Energy 16 | 17 | ----------------------------------------------------------------------- 18 | Arguments: 19 | predicted: 20 | true: 21 | bias: 22 | """ 23 | 24 | self.predicted = predicted 25 | self.true = true 26 | self.bias = bias 27 | self.inherited_bias = inherited_bias 28 | 29 | @property 30 | def delta(self) -> float: 31 | """ 32 | Difference between true and predicted energies 33 | 34 | ----------------------------------------------------------------------- 35 | Returns: 36 | (float): E_true - E_predicted 37 | 38 | Raises: 39 | (ValueError): If at least one energy is not defined 40 | """ 41 | 42 | if self.true is None: 43 | raise ValueError('Cannot calculate ∆E. No true energy') 44 | 45 | if self.predicted is None: 46 | raise ValueError('Cannot calculate ∆E. No predicted energy') 47 | 48 | return self.true - self.predicted 49 | -------------------------------------------------------------------------------- /mlptrain/forces.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from typing import Optional 3 | 4 | 5 | class Forces: 6 | """Forces in units of eV / Å""" 7 | 8 | def __init__( 9 | self, 10 | predicted: Optional[np.ndarray] = None, 11 | true: Optional[np.ndarray] = None, 12 | ): 13 | """ 14 | Forces 15 | 16 | ----------------------------------------------------------------------- 17 | Arguments: 18 | predicted: 19 | true: 20 | """ 21 | 22 | self.predicted = predicted 23 | self.true = true 24 | 25 | @property 26 | def delta(self) -> np.ndarray: 27 | """ 28 | Difference between true and predicted forces 29 | 30 | ----------------------------------------------------------------------- 31 | Returns: 32 | (np.ndarray): F_true - F_predicted. Shape = (n_atoms, 3) 33 | 34 | Raises: 35 | (ValueError): If at least one set of forces is not defined 36 | """ 37 | 38 | if self.true is None: 39 | raise ValueError('Cannot calculate ∆F. No true forces') 40 | 41 | if self.predicted is None: 42 | raise ValueError('Cannot calculate ∆F. No predicted forces') 43 | 44 | if self.true.shape != self.predicted.shape: 45 | raise ValueError('Cannot calculate ∆F. Shape mismatch') 46 | 47 | return self.true - self.predicted 48 | -------------------------------------------------------------------------------- /mlptrain/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | ll = os.environ.get('MLT_LOG_LEVEL', default='INFO') 5 | 6 | logging.basicConfig( 7 | level=getattr(logging, ll), 8 | format='%(name)-12s: %(levelname)-8s %(message)s', 9 | ) 10 | logger = logging.getLogger(__name__) 11 | 12 | # Try and use colourful logs 13 | try: 14 | import coloredlogs 15 | 16 | coloredlogs.install(level=getattr(logging, ll), logger=logger) 17 | except ImportError: 18 | pass 19 | -------------------------------------------------------------------------------- /mlptrain/loss/__init__.py: -------------------------------------------------------------------------------- 1 | from mlptrain.loss.tau import TauCalculator 2 | from mlptrain.loss.mean_errors import RMSE, MAD 3 | 4 | __all__ = ['TauCalculator', 'RMSE', 'MAD'] 5 | -------------------------------------------------------------------------------- /mlptrain/loss/_base.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | from abc import ABC, abstractmethod 3 | from typing import Optional 4 | 5 | 6 | class LossValue(ABC, float): 7 | def __init__(self, x, error: Optional[float] = None): 8 | """ 9 | Loss value with a possible associated error 10 | 11 | ----------------------------------------------------------------------- 12 | Arguments: 13 | x (float | int): Value 14 | 15 | Keyword Arguments: 16 | error (float | None): 17 | """ 18 | 19 | float.__init__(float(x)) 20 | self.error: Optional[float] = error 21 | 22 | @abstractmethod 23 | def __repr__(self) -> str: 24 | """Representation of this loss""" 25 | 26 | @property 27 | def _err_str(self) -> str: 28 | """String containing the value and any associated error""" 29 | return '' if self.error is None else f'±{self.error}' 30 | 31 | 32 | class LossFunction(ABC): 33 | def __init__(self, method_name: Optional[str] = None): 34 | """ 35 | Construct a loss function 36 | 37 | ----------------------------------------------------------------------- 38 | Arguments: 39 | method_name: Name of the reference method to evaluate true 40 | energies and forces 41 | """ 42 | 43 | self.method_name = method_name 44 | 45 | @abstractmethod 46 | def __call__( 47 | self, 48 | configurations: 'mlptrain.ConfigurationSet', 49 | mlp: 'mlptrain.potentials._base.MLPotential', 50 | **kwargs, 51 | ) -> LossValue: 52 | """Compute a loss value""" 53 | -------------------------------------------------------------------------------- /mlptrain/loss/mean_errors.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | import numpy as np 3 | from abc import ABC, abstractmethod 4 | from mlptrain.loss._base import LossValue, LossFunction 5 | 6 | 7 | class _DeltaLossFunction(LossFunction, ABC): 8 | """Error measure that depends on E_true - E_predicted""" 9 | 10 | loss_type = None 11 | 12 | def __call__( 13 | self, 14 | configurations: 'mlptrain.ConfigurationSet', 15 | mlp: 'mlptrain.potentials.MLPotential', 16 | **kwargs, 17 | ) -> LossValue: 18 | """Calculate the value of the loss 19 | 20 | ----------------------------------------------------------------------- 21 | Arguments: 22 | configurations: Set of configurations to evaluate over 23 | 24 | mlp: Potential to use 25 | """ 26 | from scipy.stats import bootstrap 27 | 28 | if self.loss_type is None: 29 | raise NotImplementedError(f'{self} did not define loss_type') 30 | 31 | if 'method_name' in kwargs: 32 | self.method_name = kwargs.pop('method_name') 33 | 34 | if len(kwargs) > 0: 35 | raise ValueError(f'Unknown keyword arguments: {kwargs}') 36 | 37 | delta_Es = self._delta_energies(configurations, mlp) 38 | std_error = bootstrap(delta_Es, self.statistic).standard_error 39 | 40 | return self.loss_type(self.statistic(delta_Es), error=std_error) 41 | 42 | def _delta_energies(self, cfgs, mlp): 43 | """Evaluate E_true - E_predicted along a set of configurations""" 44 | 45 | for idx, configuration in enumerate(cfgs): 46 | if configuration.energy.true is None: 47 | if self.method_name is not None: 48 | configuration.single_point(method=self.method_name) 49 | 50 | else: 51 | raise RuntimeError( 52 | f'Cannot compute loss for configuration ' 53 | f'{idx}, a true energy was not present' 54 | ) 55 | 56 | if configuration.energy.predicted is None: 57 | mlp.predict(configuration) 58 | 59 | return np.array([c.energy.delta for c in cfgs]) 60 | 61 | @staticmethod 62 | @abstractmethod 63 | def statistic(arr: np.ndarray) -> float: 64 | """Error measure over an array of values""" 65 | 66 | 67 | class RMSEValue(LossValue): 68 | def __repr__(self): 69 | return f'RMSE({float.__repr__(self)}{self._err_str})' 70 | 71 | 72 | class RMSE(_DeltaLossFunction): 73 | """RMSE = √(1/N Σ_i (y_i^predicted - y_i^true)^2)""" 74 | 75 | loss_type = RMSEValue 76 | 77 | @staticmethod 78 | def statistic(arr: np.ndarray) -> float: 79 | return np.sqrt(np.mean(np.square(arr))) 80 | 81 | 82 | class MADValue(LossValue): 83 | def __repr__(self): 84 | return f'MAD({float.__repr__(self)}{self._err_str})' 85 | 86 | 87 | class MAD(LossFunction): 88 | """MAD = 1/N √(Σ_i |y_i^predicted - y_i^true|)""" 89 | 90 | loss_type = MADValue 91 | 92 | @staticmethod 93 | def statistic(arr: np.ndarray) -> float: 94 | return float(np.mean(np.abs(arr))) 95 | -------------------------------------------------------------------------------- /mlptrain/loss/tau.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | import numpy as np 3 | from typing import Optional 4 | from mlptrain.sampling.md import run_mlp_md 5 | from mlptrain.log import logger 6 | from mlptrain.config import Config 7 | from mlptrain.loss._base import LossFunction, LossValue 8 | 9 | 10 | class Tau(LossValue): 11 | def __repr__(self): 12 | return f'τ_acc = {float.__repr__(self)}{self._err_str}' 13 | 14 | 15 | class TauCalculator(LossFunction): 16 | def __init__( 17 | self, 18 | e_lower: float = 0.1, 19 | e_thresh: Optional[float] = None, 20 | max_time: float = 1000.0, 21 | time_interval: float = 50.0, 22 | temp: float = 300.0, 23 | dt: float = 0.5, 24 | ): 25 | """ 26 | τ_acc prospective error metric in fs 27 | 28 | ---------------------------------------------------------------------- 29 | Arguments: 30 | 31 | e_lower: (float) E_l energy threshold in eV below which 32 | the error is zero-ed, i.e. the acceptable level of 33 | error possible in the system 34 | 35 | e_thresh: (float | None) E_t total cumulative error in eV. τ_acc 36 | is defined at the time in the simulation where this 37 | threshold is exceeded. If None then: 38 | e_thresh = 10 * e_lower 39 | 40 | max_time: (float) Maximum time in femto-seconds for τ_acc 41 | 42 | time_interval: (float) Interval between which |E_true - E_GAP| is 43 | calculated. Must be at least one timestep 44 | 45 | temp: (float) Temperature of the simulation to perform 46 | 47 | dt: (float) Timestep of the simulation in femto-seconds 48 | """ 49 | super().__init__() 50 | 51 | if time_interval < dt: 52 | raise ValueError( 53 | 'The calculated interval must be more than a ' 54 | 'single timestep' 55 | ) 56 | 57 | self.dt = float(dt) 58 | self.temp = float(temp) 59 | self.max_time = float(max_time) 60 | self.time_interval = float(time_interval) 61 | 62 | self.e_l = float(e_lower) 63 | self.e_t = 10 * self.e_l if e_thresh is None else float(e_thresh) 64 | 65 | logger.info( 66 | 'Successfully initialised τ_acc, will do a maximum of ' 67 | f'{int(self.max_time // self.time_interval)} reference ' 68 | f'calculations' 69 | ) 70 | 71 | def __call__( 72 | self, 73 | configurations: 'mlptrain.ConfigurationSet', 74 | mlp: 'mlptrain.potentials._base.MLPotential', 75 | **kwargs, 76 | ) -> Tau: 77 | """ 78 | Calculate τ_acc from a set of initial configurations 79 | 80 | ----------------------------------------------------------------------- 81 | Arguments: 82 | configurations: A set of initial configurations from which dynamics 83 | will be propagated from 84 | 85 | mlp: Machine learnt potential 86 | 87 | Returns: 88 | (Tau): τ_acc 89 | """ 90 | if len(configurations) < 2: 91 | raise ValueError( 92 | f'Cannot calculate τ_acc over only ' 93 | f'{len(configurations)} configurations. Need > 1' 94 | ) 95 | 96 | if 'method_name' not in kwargs: 97 | raise ValueError( 98 | 'Cannot compute τ_acc without a method. Please ' 99 | 'specify e.g. calc(..., method_name="orca")' 100 | ) 101 | 102 | taus = [ 103 | self._calculate_single(c, mlp, kwargs['method_name']) 104 | for c in configurations 105 | ] 106 | 107 | # Calculate τ_acc as the average ± the standard error in the mean 108 | return Tau( 109 | np.average(taus), error=np.std(taus) / np.sqrt(len(taus) - 1) 110 | ) 111 | 112 | def _calculate_single(self, config, mlp, method_name): 113 | """Calculate a single τ_acc from one configuration""" 114 | 115 | cuml_error, curr_time = 0, 0 116 | block_time = self.time_interval * Config.n_cores 117 | 118 | while curr_time < self.max_time: 119 | traj = run_mlp_md( 120 | config, 121 | mlp=mlp, 122 | temp=self.temp, 123 | dt=self.dt, 124 | interval=int(self.time_interval / self.dt), 125 | fs=block_time, 126 | n_cores=min(Config.n_cores, 4), 127 | ) 128 | 129 | try: 130 | traj.single_point(method_name) 131 | except (ValueError, TypeError): 132 | logger.warning( 133 | 'Failed to calculate single point energies with' 134 | f' {method_name}. τ_acc will be underestimated ' 135 | f'by <{block_time}' 136 | ) 137 | return curr_time 138 | 139 | logger.info(' ___ |E_true - E_GAP|/eV ___') 140 | logger.info(' t/fs err cumul(err)') 141 | 142 | for i, frame in enumerate(traj): 143 | if frame.energy.true is None: 144 | logger.warning(f'Frame {i} had no energy') 145 | e_error = np.inf 146 | else: 147 | e_error = abs(frame.energy.delta) 148 | 149 | # Add any error above the allowed threshold 150 | cuml_error += max(e_error - self.e_l, 0) 151 | curr_time += self.time_interval 152 | logger.info( 153 | f'{curr_time:5.0f} ' 154 | f'{e_error:6.4f} ' 155 | f'{cuml_error:6.4f}' 156 | ) 157 | 158 | if cuml_error > self.e_t: 159 | return curr_time 160 | 161 | config = traj[-1] 162 | 163 | logger.info(f'Reached max(τ_acc) = {self.max_time} fs') 164 | return self.max_time 165 | -------------------------------------------------------------------------------- /mlptrain/molecule.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | import numpy as np 3 | import autode as ade 4 | from scipy.spatial.distance import cdist 5 | 6 | 7 | class Molecule(ade.Molecule): 8 | @property 9 | def centroid(self) -> np.ndarray: 10 | """ 11 | Centroid of this molecule 12 | 13 | ----------------------------------------------------------------------- 14 | Returns: 15 | (np.ndarray): shape = (3,) 16 | """ 17 | return np.average(self.coordinates, axis=0) 18 | 19 | def is_in_box(self, box: 'mlptrain.box.Box') -> bool: 20 | """Is this molecule totally inside a box with an origin at 21 | (0,0,0) and top right corner (a, b, c) = box.size 22 | 23 | ----------------------------------------------------------------------- 24 | Arguments: 25 | box: 26 | 27 | Returns: 28 | (bool): 29 | """ 30 | coords = self.coordinates 31 | 32 | if np.min(coords) < 0.0: 33 | return False 34 | 35 | # Maximum x, y, z component of all atoms should be < a, b, c 36 | if max(np.max(coords, axis=0) - box.size) > 0: 37 | return False 38 | 39 | return True 40 | 41 | def min_distance_to(self, coords: np.ndarray) -> float: 42 | """Calculate the minimum distance from this molecule to a set 43 | of coordinates 44 | 45 | ----------------------------------------------------------------------- 46 | Arguments: 47 | coords: shape = (n, 3) 48 | 49 | Returns: 50 | (float): Minimum distance (Å) 51 | """ 52 | # Infinite distance to the other set if there are no coordinates 53 | if len(coords) == 0: 54 | return np.inf 55 | 56 | return np.min(cdist(coords, self.coordinates)) 57 | 58 | def random_normal_jiggle(self, sigma: float = 0.01) -> None: 59 | """ 60 | Add a random displacement to each atoms position. 61 | 62 | ----------------------------------------------------------------------- 63 | Arguments: 64 | sigma: Standard deviation of the standard deviation 65 | """ 66 | dx = np.random.normal( 67 | scale=sigma, # Å 68 | loc=0.0, 69 | size=(self.n_atoms, 3), 70 | ) 71 | 72 | self.coordinates += dx 73 | 74 | return None 75 | -------------------------------------------------------------------------------- /mlptrain/potentials/__init__.py: -------------------------------------------------------------------------------- 1 | from mlptrain.potentials.gap.gap import GAP 2 | from mlptrain.potentials.ace.ace import ACE 3 | from mlptrain.potentials.nequip._nequip import NequIP 4 | from mlptrain.potentials.mace.mace import MACE 5 | 6 | __all__ = ['GAP', 'ACE', 'NequIP', 'MACE'] 7 | -------------------------------------------------------------------------------- /mlptrain/potentials/ace/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/mlptrain/potentials/ace/__init__.py -------------------------------------------------------------------------------- /mlptrain/potentials/gap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/mlptrain/potentials/gap/__init__.py -------------------------------------------------------------------------------- /mlptrain/potentials/mace/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mlptrain/potentials/nequip/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/mlptrain/potentials/nequip/__init__.py -------------------------------------------------------------------------------- /mlptrain/sampling/__init__.py: -------------------------------------------------------------------------------- 1 | from mlptrain.sampling.bias import Bias 2 | from mlptrain.sampling.plumed import PlumedBias, PlumedCalculator 3 | from mlptrain.sampling.umbrella import UmbrellaSampling 4 | from mlptrain.sampling.metadynamics import Metadynamics 5 | 6 | __all__ = [ 7 | 'Bias', 8 | 'PlumedBias', 9 | 'PlumedCalculator', 10 | 'UmbrellaSampling', 11 | 'Metadynamics', 12 | ] 13 | -------------------------------------------------------------------------------- /mlptrain/sampling/_base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Function(ABC): 5 | """Function defining both a image and a gradient""" 6 | 7 | @abstractmethod 8 | def __call__(self, *args, **kwargs): 9 | """Value of the function""" 10 | 11 | @abstractmethod 12 | def grad(self, *args, **kwargs): 13 | """Gradient of the function""" 14 | 15 | 16 | class ASEConstraint(ABC): 17 | """Abstract base class for an ASE constraint""" 18 | 19 | @abstractmethod 20 | def adjust_forces(self, atoms, forces): 21 | """Adjust the forces of a set of atoms using a the gradient of the bias 22 | function""" 23 | 24 | @abstractmethod 25 | def adjust_potential_energy(self, atoms): 26 | """Adjust the energy of a set of atoms using the bias function""" 27 | 28 | @abstractmethod 29 | def adjust_positions(self, atoms, newpositions): 30 | """Method required for ASE but not used in ml-train""" 31 | -------------------------------------------------------------------------------- /mlptrain/sampling/bias.py: -------------------------------------------------------------------------------- 1 | import mlptrain 2 | from mlptrain.sampling._base import Function, ASEConstraint 3 | 4 | 5 | class Bias(ASEConstraint, Function): 6 | """Modifies the forces and energy of a set of ASE atoms under a bias""" 7 | 8 | def __init__( 9 | self, 10 | zeta_func: 'mlptrain.sampling.reaction_coord.ReactionCoordinate', 11 | kappa: float, 12 | reference: float, 13 | ): 14 | """ 15 | Bias that modifies the forces and energy of a set of atoms under a 16 | harmonic bias function. 17 | 18 | Harmonic biasing potential: ω = κ/2 (ζ(r) - ζ_ref)^2 19 | 20 | e.g. bias = mlt.bias.Bias(to_average=[[0, 1]], reference=2, kappa=10) 21 | 22 | ----------------------------------------------------------------------- 23 | Arguments: 24 | 25 | zeta_func: Reaction coordinate, taking the positions of the system 26 | and returning a scalar e.g. a distance or sum of distances 27 | 28 | kappa: Value of the spring constant, κ 29 | 30 | reference: Reference value of the reaction coordinate, ζ_ref 31 | """ 32 | self.ref = reference 33 | self.kappa = kappa 34 | self.f = zeta_func 35 | 36 | def __call__(self, atoms): 37 | """Value of the bias for set of atom pairs in atoms""" 38 | 39 | return 0.5 * self.kappa * (self.f(atoms) - self.ref) ** 2 40 | 41 | def grad(self, atoms): 42 | """Gradient of the biasing potential a set of atom pairs in atoms""" 43 | 44 | return self.kappa * self.f.grad(atoms) * (self.f(atoms) - self.ref) 45 | 46 | def adjust_potential_energy(self, atoms): 47 | """Adjust the energy of a set of atoms using the bias function""" 48 | 49 | return self.__call__(atoms) 50 | 51 | def adjust_forces(self, atoms, forces): 52 | """Adjust the forces of a set of atoms in place using the gradient 53 | of the bias function:: 54 | 55 | F = -∇E -∇B 56 | 57 | where ∇E is the gradient of the energy with respect to the coordinates 58 | and B is the bias. 59 | """ 60 | forces -= self.grad(atoms) 61 | 62 | return None 63 | 64 | def adjust_positions(self, atoms, newpositions): 65 | """Method required for ASE but not used in mlp-train""" 66 | return None 67 | -------------------------------------------------------------------------------- /mlptrain/training/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/mlptrain/training/__init__.py -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.4,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "mlptrain" 7 | description = "Machine Learning Potential Training" 8 | authors = [{name = "Duarte Group"}] 9 | readme = "README.md" 10 | license = {file = "LICENSE.md"} 11 | classifiers = ["License :: OSI Approved :: MIT License"] 12 | dynamic = ["version"] 13 | requires-python = ">=3.8" 14 | 15 | [project.optional-dependencies] 16 | docs = [ 17 | 'sphinx~=7.4.7', 18 | 'sphinx-copybutton~=0.5.2', 19 | 'sphinx-rtd-theme~=3.0.2', 20 | ] 21 | 22 | [project.urls] 23 | Home = "https://github.com/duartegroup/mlp-train" 24 | Source = "https://github.com/duartegroup/mlp-train" 25 | 26 | [tool.ruff] 27 | line-length = 79 28 | 29 | [tool.ruff.format] 30 | quote-style = "single" 31 | 32 | [tool.pytest.ini_options] 33 | addopts = "--cov-report term --cov-report xml" 34 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/tests/__init__.py -------------------------------------------------------------------------------- /tests/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/tests/data/__init__.py -------------------------------------------------------------------------------- /tests/data/data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/tests/data/data.zip -------------------------------------------------------------------------------- /tests/data/methane.xyz: -------------------------------------------------------------------------------- 1 | 5 2 | 3 | C -0.83511 2.41296 0.00000 4 | H 0.23489 2.41296 0.00000 5 | H -1.19178 2.07309 0.94983 6 | H -1.19178 1.76033 -0.76926 7 | H -1.19178 3.40548 -0.18057 8 | 5 9 | 10 | C -0.83511 2.41296 0.00000 11 | H 0.29730 2.42545 0.00000 12 | H -1.19178 2.07309 0.94983 13 | H -1.19178 1.76033 -0.76926 14 | H -1.19178 3.40548 -0.18057 15 | 5 16 | 17 | C -0.83511 2.41296 0.00000 18 | H 0.23489 2.41296 0.00000 19 | H -1.40298 1.99095 0.94983 20 | H -1.19178 1.76033 -0.76926 21 | H -1.19178 3.40548 -0.18057 22 | -------------------------------------------------------------------------------- /tests/data/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | from zipfile import ZipFile 4 | from functools import wraps 5 | 6 | 7 | def unzip_dir(zip_path): 8 | return work_in_zipped_dir(zip_path, chdir=False) 9 | 10 | 11 | def work_in_zipped_dir(zip_path, chdir=True): 12 | """Extract some data from a compressed folder, change directories to it if 13 | required, run the function then, if required change directories back out 14 | and then delete the generated folder""" 15 | assert zip_path.endswith('.zip') 16 | 17 | def func_decorator(func): 18 | @wraps(func) 19 | def wrapped_function(*args, **kwargs): 20 | dir_path = zip_path[:-4] # Remove the .zip extension 21 | 22 | extract_path = os.path.split(dir_path)[0] 23 | here = os.getcwd() 24 | 25 | with ZipFile(zip_path, 'r') as zip_folder: 26 | zip_folder.extractall(extract_path) 27 | 28 | if chdir: 29 | os.chdir(dir_path) 30 | 31 | try: 32 | result = func(*args, **kwargs) 33 | 34 | finally: 35 | if chdir: 36 | os.chdir(here) 37 | 38 | shutil.rmtree(dir_path) 39 | 40 | return result 41 | 42 | return wrapped_function 43 | 44 | return func_decorator 45 | -------------------------------------------------------------------------------- /tests/data/water_01_preplumed.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duartegroup/mlp-train/bfe7026af564ca1a822f7278897837c03d0d73ea/tests/data/water_01_preplumed.npz -------------------------------------------------------------------------------- /tests/test_bias.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | 4 | import mlptrain as mlt 5 | from mlptrain.utils import work_in_tmp_dir 6 | from .test_potential import TestPotential 7 | 8 | mlt.Config.n_cores = 1 9 | here = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | def _get_avg_dists(atoms, atom_pair_list): 13 | """Return the average distance between atoms in all m pairs""" 14 | 15 | euclidean_dists = [ 16 | atoms.get_distance(i, j, mic=True) for (i, j) in atom_pair_list 17 | ] 18 | 19 | return np.mean(euclidean_dists) 20 | 21 | 22 | @work_in_tmp_dir() 23 | def test_bias(h2): 24 | system = mlt.System(h2, box=[50, 50, 50]) 25 | pot = TestPotential('1D') 26 | 27 | config = system.random_configuration() 28 | 29 | bias = mlt.Bias(mlt.AverageDistance([0, 1]), reference=0.7, kappa=100) 30 | 31 | assert bias.ref is not None 32 | assert bias.kappa is not None 33 | assert bias.f.atom_pair_list == [(0, 1)] 34 | 35 | new_pos = [[0, 0, 0], [0, 0, 1]] 36 | 37 | ase_atoms = config.ase_atoms 38 | ase_atoms.set_positions(new_pos, apply_constraint=False) 39 | 40 | assert np.isclose(bias(ase_atoms), 4.5) # (kappa / 2) * (1-0.7)^2 41 | 42 | bias_force = bias.grad(ase_atoms) 43 | 44 | assert bias_force[0][2] == -bias_force[1][[2]] 45 | assert np.isclose(bias_force[0][2], -30) # kappa * (1-0.7) 46 | 47 | trajectory = mlt.md.run_mlp_md( 48 | configuration=config, 49 | mlp=pot, 50 | fs=1000, 51 | temp=300, 52 | dt=0.5, 53 | interval=10, 54 | bias=bias, 55 | ) 56 | 57 | data = [ 58 | _get_avg_dists(config.ase_atoms, [[0, 1]]) for config in trajectory 59 | ] 60 | 61 | hist, bin_edges = np.histogram(data, density=False, bins=500) 62 | mids = 0.5 * (bin_edges[1:] + bin_edges[:-1]) 63 | mean_value = np.average(mids, weights=hist) 64 | 65 | assert np.isclose(mean_value, 0.7, 0.1) 66 | 67 | trajectory.save_xyz('tmp.xyz') 68 | 69 | assert os.path.exists('tmp.xyz') 70 | -------------------------------------------------------------------------------- /tests/test_configuration.py: -------------------------------------------------------------------------------- 1 | from autode.atoms import Atom 2 | from autode.exceptions import SolventNotFound 3 | from mlptrain.configurations.configuration import ( 4 | Configuration, 5 | _random_vector_in_box, 6 | _get_max_mol_distance, 7 | ) 8 | import numpy as np 9 | import random 10 | import pytest 11 | 12 | 13 | def test_equality(): 14 | config1 = Configuration() 15 | assert config1 == config1 16 | assert config1 == Configuration() 17 | 18 | config2 = Configuration(atoms=[Atom('H')]) 19 | 20 | assert config1 != config2 21 | 22 | 23 | seeded_random = random.Random() 24 | 25 | 26 | def test_random_vector_in_box(): 27 | vector = _random_vector_in_box( 28 | 10, 29 | seeded_random.random(), 30 | seeded_random.random(), 31 | seeded_random.random(), 32 | ) 33 | assert all(v <= 10 for v in vector) 34 | assert all(v >= 0 for v in vector) 35 | 36 | 37 | def test_get_max_mol_distance(h2o_configuration): 38 | max_distance_h2o = _get_max_mol_distance(h2o_configuration.atoms) 39 | max_distance_h2o = round(max_distance_h2o, 3) 40 | assert max_distance_h2o == 1.584 41 | 42 | 43 | def test_solvate(h2o_configuration, h2o_solvated_with_h2o): 44 | h2o_configuration.solvate(solvent_name='water') 45 | assert len(h2o_configuration.atoms) == 159 46 | assert all( 47 | [ 48 | np.round(atom.coordinate, 3) 49 | == h2o_solvated_with_h2o.atoms[i].coordinate 50 | for i, atom in enumerate(h2o_configuration.atoms) 51 | ] 52 | ) 53 | 54 | 55 | def test_wrong_solvent_name_raises_not_found(h2o_configuration): 56 | with pytest.raises(SolventNotFound): 57 | h2o_configuration.solvate(solvent_name='solvo_solverson') 58 | 59 | 60 | def test_no_inputs_for_solvate(h2o_configuration): 61 | with pytest.raises(ValueError): 62 | h2o_configuration.solvate() 63 | 64 | 65 | def test_only_molecule_for_solvate(h2o_configuration, h2o): 66 | with pytest.raises(ValueError): 67 | h2o_configuration.solvate(solvent_molecule=h2o) 68 | 69 | 70 | def test_only_density_for_solvate(h2o_configuration): 71 | with pytest.raises(ValueError): 72 | h2o_configuration.solvate(solvent_density=1) 73 | 74 | 75 | def test_only_too_many_inputs_for_solvate(h2o_configuration, h2o): 76 | with pytest.raises(ValueError): 77 | h2o_configuration.solvate( 78 | solvent_name='water', solvent_density=1, solvent_molecule=h2o 79 | ) 80 | 81 | 82 | def test_negative_density_for_solvate(h2o_configuration, h2o): 83 | with pytest.raises(ValueError): 84 | h2o_configuration.solvate(solvent_molecule=h2o, solvent_density=-1) 85 | 86 | 87 | def test_no_atoms_in_solvent_molecule(h2o_configuration, empty_molecule): 88 | with pytest.raises(ValueError): 89 | h2o_configuration.solvate( 90 | solvent_density=1, solvent_molecule=empty_molecule 91 | ) 92 | -------------------------------------------------------------------------------- /tests/test_descriptor.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from autode.atoms import Atom 3 | from mlptrain.descriptor import SoapDescriptor 4 | from mlptrain import Configuration, ConfigurationSet 5 | import numpy as np 6 | 7 | 8 | @pytest.fixture 9 | def methane(): 10 | """Fixture to create a Configuration instance for methane.""" 11 | atoms = [ 12 | Atom('C', 0, 0, 0), 13 | Atom('H', 0.629118, 0.629118, 0.629118), 14 | Atom('H', -0.629118, -0.629118, 0.629118), 15 | Atom('H', 0.629118, -0.629118, -0.629118), 16 | Atom('H', -0.629118, 0.629118, -0.629118), 17 | ] 18 | return Configuration(atoms=atoms) 19 | 20 | 21 | def test_soap_descriptor_initialization(): 22 | """Test initialization of SoapDescriptor with and without elements.""" 23 | # With elements 24 | descriptor_with = SoapDescriptor( 25 | elements=['H', 'O'], r_cut=5.0, n_max=6, l_max=6 26 | ) 27 | assert descriptor_with.elements == ['H', 'O'] 28 | # Without elements (should handle dynamic element setup) 29 | descriptor_without = SoapDescriptor() 30 | assert descriptor_without.elements is None 31 | 32 | 33 | def test_compute_representation(h2o_configuration): 34 | """Test computation of SOAP representation for water""" 35 | descriptor = SoapDescriptor( 36 | elements=['H', 'O'], r_cut=5.0, n_max=6, l_max=6 37 | ) 38 | representation = descriptor.compute_representation(h2o_configuration) 39 | # Directly check against the actual observed output shape 40 | assert representation.shape == ( 41 | 1, 42 | 546, 43 | ), f'Expected shape (1, 546), but got {representation.shape}' 44 | 45 | 46 | def test_kernel_vector_identical_molecules(h2o_configuration): 47 | descriptor = SoapDescriptor( 48 | elements=['H', 'O'], r_cut=5.0, n_max=6, l_max=6 49 | ) 50 | kernel_vector = descriptor.kernel_vector( 51 | h2o_configuration, h2o_configuration, zeta=4 52 | ) 53 | assert np.allclose(kernel_vector, np.ones_like(kernel_vector), atol=1e-5) 54 | 55 | 56 | def test_kernel_vector_different_molecules(h2o_configuration, methane): 57 | descriptor = SoapDescriptor( 58 | elements=['H', 'C', 'O'], r_cut=5.0, n_max=6, l_max=6, average='inner' 59 | ) 60 | configurations = ConfigurationSet(h2o_configuration, methane) 61 | kernel_vector = descriptor.kernel_vector( 62 | h2o_configuration, configurations, zeta=4 63 | ) 64 | expected_value = [1.0, 0.29503] 65 | assert np.allclose( 66 | kernel_vector, expected_value, atol=1e-3 67 | ), f'Expected vector {expected_value}, but got {kernel_vector}' 68 | -------------------------------------------------------------------------------- /tests/test_md.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import mlptrain as mlt 4 | from ase.io.trajectory import Trajectory as ASETrajectory 5 | from ase.constraints import Hookean 6 | from .test_potential import TestPotential 7 | from .data.utils import work_in_zipped_dir 8 | 9 | here = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 13 | def test_md_full_plumed_input(h2o_configuration): 14 | bias = mlt.PlumedBias(filename='plumed_bias_nopath.dat') 15 | 16 | mlt.md.run_mlp_md( 17 | configuration=h2o_configuration, 18 | mlp=TestPotential('1D'), 19 | temp=300, 20 | dt=1, 21 | interval=10, 22 | bias=bias, 23 | kept_substrings=['.dat'], 24 | ps=1, 25 | ) 26 | 27 | assert os.path.exists('colvar.dat') 28 | assert os.path.exists('HILLS.dat') 29 | 30 | 31 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 32 | def test_md_restart(h2_configuration): 33 | atoms = h2_configuration.ase_atoms 34 | initial_trajectory = ASETrajectory('md_restart.traj', 'r', atoms) 35 | 36 | mlt.md.run_mlp_md( 37 | configuration=h2_configuration, 38 | mlp=TestPotential('1D'), 39 | temp=300, 40 | dt=1, 41 | interval=10, 42 | restart_files=['md_restart.traj'], 43 | ps=1, 44 | ) 45 | 46 | assert os.path.exists('md_restart.traj') 47 | 48 | final_trajectory = ASETrajectory('md_restart.traj', 'r', atoms) 49 | 50 | # 10 ps simulation with dt = 1 fs and interval of 10 -> 1001 frames 51 | assert len(initial_trajectory) == 1001 52 | 53 | # Adding 1 ps simulation with interval 10 -> 101 frames, but removing one 54 | # duplicate frame 55 | assert len(final_trajectory) == 1001 + 101 - 1 56 | 57 | 58 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 59 | def test_md_save(h2_configuration): 60 | mlt.md.run_mlp_md( 61 | configuration=h2_configuration, 62 | mlp=TestPotential('1D'), 63 | temp=300, 64 | dt=1, 65 | interval=10, 66 | kept_substrings=['.traj'], 67 | ps=1, 68 | save_fs=200, 69 | ) 70 | 71 | assert os.path.exists('trajectory.traj') 72 | 73 | assert not os.path.exists('trajectory_0fs.traj') 74 | assert os.path.exists('trajectory_200fs.traj') 75 | assert os.path.exists('trajectory_1000fs.traj') 76 | assert not os.path.exists('trajectory_1200fs.traj') 77 | 78 | traj_200fs = ASETrajectory('trajectory_200fs.traj') 79 | 80 | # 200 ps / 10 interval == 20 frames; + 1 starting frame 81 | assert len(traj_200fs) == 20 + 1 82 | 83 | 84 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 85 | def test_md_traj_attachments(h2o_configuration): 86 | cv1 = mlt.PlumedAverageCV('cv1', (0, 1)) 87 | bias = mlt.PlumedBias(cvs=cv1) 88 | 89 | hookean_constraint = Hookean(a1=1, a2=2, k=100, rt=0.5) 90 | 91 | traj = mlt.md.run_mlp_md( 92 | configuration=h2o_configuration, 93 | mlp=TestPotential('1D'), 94 | temp=300, 95 | dt=1, 96 | interval=10, 97 | bias=bias, 98 | kept_substrings=['colvar_cv1.dat'], 99 | constraints=[hookean_constraint], 100 | ps=1, 101 | ) 102 | 103 | plumed_coordinates = np.loadtxt('colvar_cv1.dat', usecols=1) 104 | 105 | for i, config in enumerate(traj): 106 | assert np.shape(config.plumed_coordinates) == (1,) 107 | assert config.plumed_coordinates[0] == plumed_coordinates[i] 108 | 109 | assert all(bias_energy is not None for bias_energy in traj.bias_energies) 110 | assert any(bias_energy != 0 for bias_energy in traj.bias_energies) 111 | -------------------------------------------------------------------------------- /tests/test_plotting.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from autode.atoms import Atom 3 | from mlptrain.configurations import ConfigurationSet, Configuration 4 | from mlptrain.configurations.plotting import parity_plot 5 | from mlptrain.utils import work_in_tmp_dir 6 | 7 | 8 | @work_in_tmp_dir() 9 | def test_parity_plot(): 10 | config_set = ConfigurationSet(allow_duplicates=True) 11 | 12 | for i in range(1000): 13 | config = Configuration(atoms=[Atom('H'), Atom('C'), Atom('C')]) 14 | 15 | # energies 16 | config.energy.true = 0.0 + (1.0 * i) 17 | config.energy.predicted = 1.0 + (1.0 * i) 18 | 19 | # forces 20 | config.forces.true = np.random.uniform(1, 100, 3).reshape(1, 3) 21 | config.forces.predicted = np.random.uniform(1, 100, 3).reshape(1, 3) 22 | 23 | config_set.append(config) 24 | 25 | # simply check there are no issues running this code 26 | parity_plot(config_set) 27 | -------------------------------------------------------------------------------- /tests/test_plumed.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import mlptrain as mlt 4 | from .data.utils import work_in_zipped_dir 5 | 6 | here = os.path.abspath(os.path.dirname(__file__)) 7 | 8 | 9 | def test_plumed_cv_from_atom_groups(): 10 | cv1 = mlt.PlumedDifferenceCV('cv1', ((0, 1), (2, 3))) 11 | 12 | assert cv1.name == 'cv1' 13 | assert cv1.units == 'Å' 14 | assert cv1.dof_names == ['cv1_dist1', 'cv1_dist2'] 15 | assert cv1.setup == [ 16 | 'cv1_dist1: DISTANCE ATOMS=1,2', 17 | 'cv1_dist2: DISTANCE ATOMS=3,4', 18 | 'cv1: CUSTOM ' 19 | 'ARG=cv1_dist1,cv1_dist2 ' 20 | 'VAR=cv1_dist1,cv1_dist2 ' 21 | 'FUNC=cv1_dist1-cv1_dist2 ' 22 | 'PERIODIC=NO', 23 | ] 24 | 25 | cv2 = mlt.PlumedAverageCV('cv2', (0, 1, 2)) 26 | 27 | assert cv2.name == 'cv2' 28 | assert cv2.units == 'rad' 29 | assert cv2.dof_names == ['cv2_ang1'] 30 | assert cv2.setup == [ 31 | 'cv2_ang1: ANGLE ATOMS=1,2,3', 32 | 'cv2: CUSTOM ' 33 | 'ARG=cv2_ang1 ' 34 | 'VAR=cv2_ang1 ' 35 | 'FUNC=1.0*(cv2_ang1) ' 36 | 'PERIODIC=NO', 37 | ] 38 | 39 | with pytest.raises(TypeError): 40 | mlt.PlumedAverageCV('') 41 | 42 | with pytest.raises(TypeError): 43 | mlt.PlumedAverageCV('', 0) 44 | 45 | with pytest.raises(TypeError): 46 | mlt.PlumedAverageCV('', ()) 47 | 48 | with pytest.raises(ValueError): 49 | mlt.PlumedAverageCV('', (1,)) 50 | 51 | with pytest.raises(NotImplementedError): 52 | mlt.PlumedAverageCV('', [(0, 1, 2, 3, 4, 5), (1, 2, 3)]) 53 | 54 | with pytest.raises(ValueError): 55 | mlt.PlumedDifferenceCV('', ((0, 1), (2, 3), (4, 5))) 56 | 57 | 58 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 59 | def test_plumed_cv_from_file(): 60 | cv1 = mlt.PlumedCustomCV( 61 | 'plumed_cv_custom.dat', component='spath', units='Å' 62 | ) 63 | 64 | assert cv1.name == 'p1.spath' 65 | assert cv1.units == 'Å' 66 | assert cv1.setup == [ 67 | 'p1: PATH ' 'REFERENCE=path.pdb ' 'TYPE=OPTIMAL ' 'LAMBDA=500.0' 68 | ] 69 | 70 | with open('path.pdb', 'r') as f: 71 | data1 = f.read() 72 | 73 | assert cv1.files == [('path.pdb', data1)] 74 | 75 | os.remove('path.pdb') 76 | cv1.write_files() 77 | 78 | with open('path.pdb', 'r') as f: 79 | data2 = f.read() 80 | 81 | assert data1 == data2 82 | 83 | 84 | def test_plumed_cv_walls(): 85 | cv1 = mlt.PlumedDifferenceCV('cv1', ((0, 1), (2, 3))) 86 | 87 | cv1.attach_lower_wall(location=1, kappa=150.0, exp=3) 88 | cv1.attach_upper_wall(location=3, kappa=150.0, exp=3) 89 | 90 | assert cv1.setup == [ 91 | 'cv1_dist1: DISTANCE ATOMS=1,2', 92 | 'cv1_dist2: DISTANCE ATOMS=3,4', 93 | 'cv1: CUSTOM ' 94 | 'ARG=cv1_dist1,cv1_dist2 ' 95 | 'VAR=cv1_dist1,cv1_dist2 ' 96 | 'FUNC=cv1_dist1-cv1_dist2 ' 97 | 'PERIODIC=NO', 98 | 'LOWER_WALLS ARG=cv1 AT=1 KAPPA=150.0 EXP=3', 99 | 'UPPER_WALLS ARG=cv1 AT=3 KAPPA=150.0 EXP=3', 100 | ] 101 | 102 | with pytest.raises(TypeError): 103 | cv1.attach_lower_wall(location=0.5, kappa=150.0, exp=3) 104 | 105 | 106 | def test_plumed_bias_from_cvs(): 107 | cv1 = mlt.PlumedAverageCV('cv1', [(0, 1, 2, 3)]) 108 | cv2 = mlt.PlumedAverageCV('cv2', [(4, 5, 6, 7)]) 109 | 110 | bias = mlt.PlumedBias((cv1, cv2)) 111 | 112 | with pytest.raises(ValueError): 113 | bias._set_metad_params( 114 | pace=10, width=(0.2, 0.3), height=0.5, biasfactor=0.5 115 | ) 116 | 117 | with pytest.raises(ValueError): 118 | bias._set_metad_params(pace=10, width=0.2, height=0.5, biasfactor=2) 119 | 120 | bias.initialise_for_metad_al( 121 | pace=10, 122 | width=(0.2, 0.3), 123 | height=0.5, 124 | biasfactor=2, 125 | grid_min=(0.5, 1.5), 126 | grid_max=(0.6, 1.6), 127 | ) 128 | 129 | assert bias.cvs == (cv1, cv2) 130 | assert bias.pace == 10 131 | assert bias.width == (0.2, 0.3) 132 | assert bias.height == 0.5 133 | assert bias.biasfactor == 2 134 | assert bias.metad_grid_min == (0.5, 1.5) 135 | assert bias.metad_grid_max == (0.6, 1.6) 136 | assert bias.metad_grid_bin is None 137 | 138 | assert bias.metad_grid_setup == 'GRID_MIN=0.5,1.5 GRID_MAX=0.6,1.6 ' 139 | 140 | bias.strip() 141 | 142 | for attribute, value in bias.__dict__.items(): 143 | if attribute == 'cvs': 144 | assert value is not None 145 | 146 | else: 147 | assert value is None 148 | 149 | 150 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 151 | def test_plumed_bias_from_file(): 152 | bias = mlt.PlumedBias(filename='plumed_bias.dat') 153 | 154 | assert bias.setup == [ 155 | 'dof1: DISTANCE ATOMS=1,2', 156 | 'dof2: DISTANCE ATOMS=2,3', 157 | 'cv1: CUSTOM ARG=dof1,dof2 VAR=dof1,dof2 ' 158 | 'FUNC=dof2-dof1 PERIODIC=NO', 159 | 'lwall: LOWER_WALLS ARG=cv1 AT=1 KAPPA=150.0 EXP=3', 160 | 'p1: PATH REFERENCE=path.pdb TYPE=OPTIMAL ' 'LAMBDA=500.0', 161 | 'UPPER_WALLS ARG=cv1 AT=3 KAPPA=150.0 EXP=3', 162 | 'METAD ARG=cv1,p1.spath PACE=100 HEIGHT=0.1 ' 163 | 'SIGMA=0.5 BIASFACTOR=4 FILE=HILLS.dat', 164 | 'PRINT ARG=cv1,p1.spath FILE=colvar.dat STRIDE=10', 165 | ] 166 | 167 | with open('path.pdb', 'r') as f: 168 | data1 = f.read() 169 | 170 | assert bias.cv_files == [('path.pdb', data1)] 171 | 172 | os.remove('path.pdb') 173 | bias.write_cv_files() 174 | 175 | with open('path.pdb', 'r') as f: 176 | data2 = f.read() 177 | 178 | assert data1 == data2 179 | 180 | bias.strip() 181 | 182 | assert bias.setup == [ 183 | 'dof1: DISTANCE ATOMS=1,2', 184 | 'dof2: DISTANCE ATOMS=2,3', 185 | 'cv1: CUSTOM ARG=dof1,dof2 VAR=dof1,dof2 ' 186 | 'FUNC=dof2-dof1 PERIODIC=NO', 187 | 'lwall: LOWER_WALLS ARG=cv1 AT=1 KAPPA=150.0 EXP=3', 188 | 'p1: PATH REFERENCE=path.pdb TYPE=OPTIMAL ' 'LAMBDA=500.0', 189 | 'UPPER_WALLS ARG=cv1 AT=3 KAPPA=150.0 EXP=3', 190 | ] 191 | 192 | 193 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 194 | def test_plumed_plot(): 195 | colvar1 = 'test_plumed_plot/colvar1.dat' 196 | colvar2 = 'test_plumed_plot/colvar2.dat' 197 | 198 | mlt.plot_cv_versus_time( 199 | filename=colvar1, 200 | time_units='fs', 201 | cv_units='Å', 202 | cv_limits=(0.5, 1.5), 203 | label='0', 204 | ) 205 | 206 | assert os.path.exists('cv1_0.pdf') 207 | 208 | mlt.plot_cv1_and_cv2( 209 | filenames=(colvar1, colvar2), 210 | cvs_units=('Å', 'Å'), 211 | cvs_limits=((0.5, 1.5), (0.5, 1.5)), 212 | label='0', 213 | ) 214 | 215 | assert os.path.exists('cv1_cv2_0.pdf') 216 | -------------------------------------------------------------------------------- /tests/test_potential.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from mlptrain.potentials._base import MLPotential 3 | from ase.calculators.calculator import Calculator 4 | from ase.calculators.lj import LennardJones 5 | 6 | 7 | class HarmonicPotential(Calculator): 8 | __test__ = False 9 | 10 | def get_potential_energy(self, atoms): 11 | r = atoms.get_distance(0, 1) 12 | 13 | return (r - 1) ** 2 14 | 15 | def get_forces(self, atoms): 16 | derivative = np.zeros((len(atoms), 3)) 17 | 18 | r = atoms.get_distance(0, 1) 19 | 20 | x_dist, y_dist, z_dist = [ 21 | atoms[0].position[j] - atoms[1].position[j] for j in range(3) 22 | ] 23 | 24 | x_i, y_i, z_i = (x_dist / r), (y_dist / r), (z_dist / r) 25 | 26 | derivative[0] = [x_i, y_i, z_i] 27 | derivative[1] = [-x_i, -y_i, -z_i] 28 | 29 | force = -2 * derivative * (r - 1) 30 | 31 | return force 32 | 33 | 34 | class TestPotential(MLPotential): 35 | __test__ = False 36 | 37 | def __init__(self, name: str, calculator='harmonic', system=None): 38 | super().__init__(name=name, system=system) 39 | self.calculator = calculator.lower() 40 | 41 | @property 42 | def ase_calculator(self): 43 | if self.calculator == 'harmonic': 44 | return HarmonicPotential() 45 | 46 | if self.calculator == 'lj': 47 | return LennardJones(rc=2.5, r0=3.0) 48 | 49 | else: 50 | raise NotImplementedError( 51 | f'{self.calculator} is not implemented ' f'as a test potential' 52 | ) 53 | 54 | def _train(self) -> None: 55 | """ABC for MLPotential required but unused in TestPotential""" 56 | 57 | def requires_atomic_energies(self) -> None: 58 | """ABC for MLPotential required but unused in TestPotential""" 59 | 60 | def requires_non_zero_box_size(self) -> None: 61 | """ABC for MLPotential required but unused in TestPotential""" 62 | -------------------------------------------------------------------------------- /tests/test_reaction_coord.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | 4 | import mlptrain as mlt 5 | from ase.atoms import Atoms as ASEAtoms 6 | 7 | 8 | def test_differencedistance(h2o_configuration): 9 | """Test the DifferenceDistance class for reaction coordinate""" 10 | 11 | config = h2o_configuration 12 | atoms = config.ase_atoms 13 | 14 | diff_dist = mlt.DifferenceDistance((0, 1), (0, 2)) 15 | 16 | # Reaction coordinate should contain two pairs of atoms 17 | assert len(diff_dist.atom_pair_list) == 2 18 | 19 | rxn_coord = diff_dist(atoms) 20 | 21 | # Check calling the class returns the reaction coordinate 22 | assert np.isclose(rxn_coord, 0.614, 0.1) 23 | 24 | grad = diff_dist.grad(atoms) 25 | 26 | # Check gradient is close to expected gradient 27 | assert np.isclose(grad[0][0], -0.1835, 0.1) 28 | 29 | # Gradient matrix should consist of N atoms multiplied by 3 (x, y, z) 30 | assert grad.shape == (len(atoms), 3) 31 | 32 | # mlp-train should raise a ValueError when != 2 atoms are specified 33 | with pytest.raises(ValueError): 34 | mlt.DifferenceDistance((0, 1, 2), (0, 1)) 35 | 36 | # mlp-train should raise a ValueError when != 2 pairs are specified 37 | with pytest.raises(ValueError): 38 | mlt.DifferenceDistance((0, 1)) 39 | 40 | 41 | @pytest.mark.parametrize( 42 | 'rs', [[(0, 1), (0, 2)], [(1, 0), (0, 2)], [(1, 0), (2, 0)]] 43 | ) 44 | def test_differencedistance_numerical_gradient(rs, h=1e-8): 45 | """Test that the analytic gradient is correct for differencedistance""" 46 | 47 | atoms = ASEAtoms( 48 | symbols=['H', 'H', 'H'], 49 | positions=[[0.0, 0.0, 0.0], [1.0, 0.1, 0.3], [-2.0, 0.2, 0.4]], 50 | ) 51 | 52 | z = mlt.DifferenceDistance(*rs) 53 | grad = z.grad(atoms) 54 | e = z(atoms) 55 | 56 | for i in range(3): 57 | for j in range(3): 58 | # Shift to a new position, evaluate the energy and shift back 59 | atoms.positions[i, j] += h 60 | e_plus_h = z(atoms) 61 | atoms.positions[i, j] -= h 62 | 63 | num_grad_ij = (e_plus_h - e) / h 64 | 65 | assert np.isclose(grad[i, j], num_grad_ij, atol=1e-8) 66 | -------------------------------------------------------------------------------- /tests/test_selection.py: -------------------------------------------------------------------------------- 1 | import os 2 | import mlptrain as mlt 3 | from autode.atoms import Atom 4 | from mlptrain.descriptor import SoapDescriptor 5 | from mlptrain.training.selection import AtomicEnvSimilarity 6 | 7 | 8 | here = os.path.abspath(os.path.dirname(__file__)) 9 | 10 | 11 | def _similar_methane(): 12 | atoms = [ 13 | Atom('C', -0.83511, 2.41296, 0.00000), 14 | Atom('H', 0.24737, 2.41296, 0.00000), 15 | Atom('H', -1.19178, 2.07309, 0.94983), 16 | Atom('H', -1.19178, 1.76033, -0.76926), 17 | Atom('H', -1.28016, 3.36760, -0.18057), 18 | ] 19 | 20 | return mlt.Configuration(atoms=atoms) 21 | 22 | 23 | def _distorted_methane(): 24 | atoms = [ 25 | Atom('C', -0.83511, 2.41296, 0.00000), 26 | Atom('H', 0.34723, 2.42545, 0.00000), 27 | Atom('H', -1.19178, 2.07309, 0.94983), 28 | Atom('H', -1.50592, -0.01979, -0.76926), 29 | Atom('H', -1.28016, 3.36760, -0.18057), 30 | ] 31 | 32 | return mlt.Configuration(atoms=atoms) 33 | 34 | 35 | def test_selection_on_structures(): 36 | configs = mlt.ConfigurationSet() 37 | 38 | file_path = os.path.join(here, 'data', 'methane.xyz') 39 | configs.load_xyz(filename=file_path, charge=0, mult=1, box=None) 40 | 41 | SoapDescriptor1 = mlt.descriptor.SoapDescriptor( 42 | average='outer', r_cut=6.0, n_max=8, l_max=8 43 | ) 44 | SoapDescriptor2 = mlt.descriptor.SoapDescriptor( 45 | average='inner', r_cut=5.0, n_max=6, l_max=6 46 | ) 47 | 48 | selector1 = AtomicEnvSimilarity(descriptor=SoapDescriptor1, threshold=0.9) 49 | selector2 = AtomicEnvSimilarity(descriptor=SoapDescriptor2, threshold=0.9) 50 | 51 | mlp = mlt.potentials.GAP('blank') 52 | mlp.training_data = configs 53 | 54 | selector1(configuration=_similar_methane(), mlp=mlp) 55 | selector2(configuration=_similar_methane(), mlp=mlp) 56 | 57 | assert not selector1.select 58 | assert not selector2.select 59 | 60 | selector1(configuration=_distorted_methane(), mlp=mlp) 61 | selector2(configuration=_distorted_methane(), mlp=mlp) 62 | 63 | assert selector1.select 64 | assert selector2.select 65 | 66 | 67 | def test_outlier_identifier(): 68 | configs = mlt.ConfigurationSet() 69 | 70 | file_path = os.path.join(here, 'data', 'methane.xyz') 71 | configs.load_xyz(filename=file_path, charge=0, mult=1, box=None) 72 | 73 | descriptor = SoapDescriptor(average='outer', r_cut=6.0, n_max=8, l_max=8) 74 | 75 | mlp = mlt.potentials.GAP('blank') 76 | mlp.training_data = configs 77 | 78 | # Similar configuration should not be an outlier 79 | result1 = mlt.training.selection._outlier_identifier( 80 | configuration=_similar_methane(), 81 | configurations=mlp.training_data, 82 | descriptor=descriptor, 83 | dim_reduction=False, 84 | ) 85 | assert result1 == 1 86 | 87 | # Distorted configuration should be an outlier 88 | result2 = mlt.training.selection._outlier_identifier( 89 | configuration=_distorted_methane(), 90 | configurations=mlp.training_data, 91 | descriptor=descriptor, 92 | dim_reduction=False, 93 | ) 94 | assert result2 == -1 95 | -------------------------------------------------------------------------------- /tests/test_system.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | import mlptrain as mlt 4 | from mlptrain.box import Box 5 | from mlptrain.configurations import Configuration, ConfigurationSet 6 | from autode.atoms import Atom 7 | 8 | 9 | @pytest.fixture 10 | def test_system(h2o): 11 | """Create a sample system with a water molecule and a box.""" 12 | return mlt.System(h2o, box=Box([10, 10, 10])) 13 | 14 | 15 | @pytest.fixture 16 | def test_charged_system(mg): 17 | """Create a sample system with a magnesium and a box.""" 18 | return mlt.System(mg, box=Box([10, 10, 10])) 19 | 20 | 21 | @pytest.fixture 22 | def test_radical_system(oh_radical): 23 | """Create a sample system with a water molecule and a box.""" 24 | return mlt.System(oh_radical, box=Box([10, 10, 10])) 25 | 26 | 27 | def test_random_configuration(test_system): 28 | """Test generating a random configuration for a system.""" 29 | config = test_system.random_configuration(min_dist=1.0) 30 | assert len(config.atoms) == sum( 31 | len(m.atoms) for m in test_system.molecules 32 | ) 33 | assert test_system != config 34 | 35 | 36 | def test_random_configurations(test_system): 37 | """Test generating multiple random configurations for a system.""" 38 | configs = test_system.random_configurations(num=5, min_dist=1.0) 39 | assert isinstance(configs, ConfigurationSet) 40 | assert len(configs) == 5 41 | for config in configs: 42 | assert isinstance(config, Configuration) 43 | assert config != test_system 44 | 45 | 46 | def test_add_molecule(test_system, h2o): 47 | """Test adding a single molecule to a system.""" 48 | initial_count = len(test_system.molecules) 49 | test_system.add_molecule(h2o) 50 | assert len(test_system.molecules) == initial_count + 1 51 | 52 | 53 | def test_add_multiple_molecules(test_system, h2o): 54 | """Test adding multiple molecules to a system.""" 55 | initial_count = len(test_system.molecules) 56 | test_system.add_molecules(h2o, num=3) 57 | assert len(test_system.molecules) == initial_count + 3 58 | 59 | 60 | def test_charge_property(test_charged_system, mg): 61 | """Test the system's total charge property.""" 62 | assert test_charged_system.charge == sum( 63 | m.charge for m in test_charged_system.molecules 64 | ) 65 | 66 | 67 | def test_mult_property(test_radical_system, oh_radical): 68 | """Test the system's total multiplicity property.""" 69 | expected_mult = 2 70 | assert test_radical_system.mult == expected_mult 71 | 72 | 73 | def test_atoms_property(test_system): 74 | """Test getting all atoms in the system.""" 75 | atoms = test_system.atoms 76 | assert isinstance(atoms, list) 77 | assert all(isinstance(atom, Atom) for atom in atoms) 78 | total_atoms = sum(len(m.atoms) for m in test_system.molecules) 79 | assert len(atoms) == total_atoms 80 | 81 | 82 | def test_unique_atomic_symbols_property(test_system): 83 | """Test getting unique atomic symbols in the system.""" 84 | unique_symbols = test_system.unique_atomic_symbols 85 | expected_symbols = set( 86 | atom.label for mol in test_system.molecules for atom in mol.atoms 87 | ) 88 | assert set(unique_symbols) == expected_symbols 89 | 90 | 91 | def test_shift_randomly_raises_runtimeerror_on_failure(test_system, h2o): 92 | """Test that _shift_randomly raises RuntimeError after max attempts.""" 93 | with pytest.raises(RuntimeError): 94 | test_system._shift_randomly( 95 | h2o, coords=np.array([[0, 0, 0]]), min_dist=100 96 | ) 97 | -------------------------------------------------------------------------------- /tests/test_trajectory.py: -------------------------------------------------------------------------------- 1 | from autode.atoms import Atom 2 | from mlptrain.configurations.configuration import Configuration 3 | from mlptrain.configurations.trajectory import Trajectory 4 | 5 | 6 | def test_trajectory_allows_duplicates(): 7 | traj = Trajectory(Configuration(atoms=[Atom('H')])) 8 | traj.append(Configuration(atoms=[Atom('H')])) 9 | assert len(traj) == 2 10 | -------------------------------------------------------------------------------- /tests/test_umbrella.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import numpy as np 5 | import pytest 6 | 7 | import mlptrain as mlt 8 | from .test_potential import TestPotential 9 | from .data.utils import work_in_zipped_dir 10 | 11 | here = os.path.abspath(os.path.dirname(__file__)) 12 | 13 | 14 | def _h2_umbrella(): 15 | return mlt.UmbrellaSampling( 16 | zeta_func=mlt.AverageDistance([0, 1]), kappa=100 17 | ) 18 | 19 | 20 | def _h2_pulled_traj(): 21 | traj = mlt.ConfigurationSet() 22 | traj.load_xyz( 23 | os.path.join(here, 'data/data', 'h2_traj.xyz'), charge=0, mult=1 24 | ) 25 | 26 | return traj 27 | 28 | 29 | def _h2_sparse_traj(): 30 | traj = _h2_pulled_traj() 31 | sparse_traj = mlt.ConfigurationSet() 32 | sparse_traj.append(traj[0]) 33 | sparse_traj.append(traj[-1]) 34 | 35 | return sparse_traj 36 | 37 | 38 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 39 | def test_run_umbrella(): 40 | umbrella = _h2_umbrella() 41 | traj = _h2_pulled_traj() 42 | n_windows = 3 43 | 44 | assert umbrella.kappa is not None and np.isclose(umbrella.kappa, 100.0) 45 | assert umbrella.zeta_refs is None 46 | 47 | # Zeta refs are now reset 48 | umbrella.run_umbrella_sampling( 49 | traj, 50 | mlp=TestPotential('1D'), 51 | temp=300, 52 | interval=5, 53 | dt=0.5, 54 | n_windows=n_windows, 55 | save_sep=False, 56 | all_to_xyz=True, 57 | fs=1000, 58 | save_fs=300, 59 | ) 60 | 61 | # Sampling with a high force constant should lead to fitted Gaussians 62 | # that closely match the reference (target) values 63 | for window in umbrella.windows: 64 | assert window.gaussian_plotted is not None 65 | assert np.isclose( 66 | window.gaussian_plotted.mean, window.zeta_ref, atol=0.1 67 | ) 68 | 69 | assert os.path.exists('trajectories') 70 | assert os.path.exists('trajectories/combined_trajectory.xyz') 71 | 72 | for idx in range(1, n_windows + 1): 73 | assert os.path.exists(f'trajectories/trajectory_{idx}.traj') 74 | 75 | for sim_time in [300, 600, 900]: 76 | assert os.path.exists( 77 | f'trajectories/trajectory_{idx}_{sim_time}fs.traj' 78 | ) 79 | assert os.path.exists( 80 | f'trajectories/window_{idx}_{sim_time}fs.xyz' 81 | ) 82 | 83 | assert os.path.exists('fitted_data.pdf') 84 | 85 | 86 | # TODO: This tests fails on GHA with MACE install, 87 | # need to investigate more, for now skipping. 88 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 89 | @pytest.mark.skip(reason='Test fails on GHA with MACE') 90 | def test_umbrella_parallel(): 91 | execution_time = {} 92 | 93 | for n_cores in (1, 2): 94 | mlt.Config.n_cores = n_cores 95 | 96 | umbrella = _h2_umbrella() 97 | traj = _h2_pulled_traj() 98 | 99 | start = time.perf_counter() 100 | umbrella.run_umbrella_sampling( 101 | traj, 102 | mlp=TestPotential('1D'), 103 | temp=300, 104 | interval=5, 105 | dt=0.5, 106 | n_windows=4, 107 | fs=500, 108 | ) 109 | finish = time.perf_counter() 110 | 111 | execution_time[n_cores] = finish - start 112 | 113 | # Calculation with more cores should run faster 114 | assert execution_time[2] < execution_time[1] 115 | 116 | 117 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 118 | def test_umbrella_sparse_traj(): 119 | umbrella = _h2_umbrella() 120 | traj = _h2_sparse_traj() 121 | n_windows = 9 122 | 123 | # Indices from 1 to 9 124 | zeta_refs = umbrella._reference_values( 125 | traj=traj, num=n_windows, final_ref=None, init_ref=None 126 | ) 127 | 128 | middle_ref = zeta_refs[5] 129 | middle_bias = mlt.Bias( 130 | zeta_func=umbrella.zeta_func, 131 | kappa=umbrella.kappa, 132 | reference=middle_ref, 133 | ) 134 | 135 | # There should be no good starting frame for the middle window (index 5) 136 | # as the sparse trajectory only contains the initial and final frame 137 | assert umbrella._no_ok_frame_in(traj, middle_ref) 138 | 139 | umbrella.run_umbrella_sampling( 140 | traj, 141 | mlp=TestPotential('1D'), 142 | temp=300, 143 | interval=5, 144 | dt=0.5, 145 | n_windows=n_windows, 146 | fs=100, 147 | save_sep=True, 148 | ) 149 | 150 | assert os.path.exists('trajectories') 151 | assert os.path.isdir('trajectories') 152 | 153 | previous_window_traj = mlt.ConfigurationSet() 154 | previous_window_traj.load_xyz( 155 | filename='trajectories/window_4.xyz', charge=0, mult=1 156 | ) 157 | 158 | middle_window_traj = mlt.ConfigurationSet() 159 | middle_window_traj.load_xyz( 160 | filename='trajectories/window_5.xyz', charge=0, mult=1 161 | ) 162 | 163 | closest_frame = umbrella._best_init_frame( 164 | bias=middle_bias, traj=previous_window_traj 165 | ) 166 | starting_frame = middle_window_traj[0] 167 | 168 | # The starting frame for the middle window (index 5) should be 169 | # the closest frame from the previous window (index 4) 170 | assert starting_frame == closest_frame 171 | 172 | 173 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 174 | def test_umbrella_save_load(): 175 | umbrella = _h2_umbrella() 176 | traj = _h2_pulled_traj() 177 | 178 | umbrella.run_umbrella_sampling( 179 | traj, 180 | mlp=TestPotential('1D'), 181 | temp=300, 182 | interval=5, 183 | dt=0.5, 184 | n_windows=3, 185 | fs=100, 186 | save_sep=False, 187 | ) 188 | 189 | umbrella.save(folder_name='tmp_us') 190 | assert os.path.exists('tmp_us') and os.path.isdir('tmp_us') 191 | 192 | loaded = mlt.UmbrellaSampling.from_folder(folder_name='tmp_us', temp=300) 193 | assert len(loaded.windows) == 3 194 | assert np.allclose(loaded.zeta_refs, umbrella.zeta_refs) 195 | 196 | for idx, window in enumerate(loaded.windows): 197 | assert np.isclose(window.zeta_ref, umbrella.zeta_refs[idx]) 198 | assert np.isclose(window._bias.kappa, 100) 199 | assert len(window._obs_zetas) == 41 200 | -------------------------------------------------------------------------------- /tests/test_wham.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | from mlptrain.sampling.bias import Bias 4 | from mlptrain.sampling.umbrella import _Window, UmbrellaSampling 5 | from .data.utils import work_in_zipped_dir 6 | 7 | here = os.path.dirname(os.path.abspath(__file__)) 8 | 9 | kj_to_ev = 0.0103642 10 | 11 | 12 | def _initialised_us() -> UmbrellaSampling: 13 | us = UmbrellaSampling(zeta_func=lambda x: None, kappa=0.0) 14 | 15 | us.temp = 300 16 | zeta_refs = np.linspace(1.8245, 3.1100, num=20) # 20 windows 17 | 18 | for window_idx in range(20): 19 | data_lines = open(f'window_{window_idx+1}.txt', 'r').readlines() 20 | 21 | # Ensure the data has the correct reference value for the hard 22 | # coded array 23 | assert np.isclose( 24 | zeta_refs[window_idx], float(data_lines[0].split()[1]) 25 | ) 26 | 27 | zeta_obs = [float(line.split()[1]) for line in data_lines[1:-1]] 28 | 29 | window = _Window( 30 | obs_zetas=np.array(zeta_obs), 31 | bias=Bias( 32 | zeta_func=None, 33 | kappa=float(data_lines[0].split()[2]), 34 | reference=zeta_refs[window_idx], 35 | ), 36 | ) 37 | 38 | us.windows.append(window) 39 | 40 | return us 41 | 42 | 43 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 44 | def test_wham_is_close_to_ref(): 45 | us = _initialised_us() 46 | zetas, free_energies = us.wham(n_bins=499) 47 | free_energies -= min(free_energies) 48 | 49 | ref_zetas = np.array( 50 | [ 51 | float(line.split()[0]) 52 | for line in open('ref_wham.txt', 'r').readlines()[1:-1] 53 | ] 54 | ) 55 | 56 | ref_free_energies = [ 57 | float(line.split()[1]) * kj_to_ev 58 | for line in open('ref_wham.txt', 'r').readlines()[1:-1] 59 | ] 60 | 61 | # Ensure every free energy value, at a particular zeta, is close to the 62 | # reference, to within ~0.5 kcal mol-1 63 | for zeta, free_energy in zip(zetas, free_energies): 64 | close_idx = np.argmin(np.abs(ref_zetas - zeta)) 65 | assert np.isclose(free_energy, ref_free_energies[close_idx], atol=0.02) 66 | 67 | 68 | @work_in_zipped_dir(os.path.join(here, 'data/data.zip')) 69 | def test_wham_is_somewhat_independent_of_nbins(): 70 | us = _initialised_us() 71 | _, free_energies_500bins = us.wham(n_bins=500) 72 | _, free_energies_1000bins = us.wham(n_bins=1000) 73 | 74 | assert np.allclose( 75 | free_energies_500bins, 76 | np.mean( 77 | free_energies_1000bins.reshape(-1, 2), axis=1 78 | ), # block average 79 | atol=5e-2, 80 | ) 81 | --------------------------------------------------------------------------------