Metadata-Version: 2.4
Name: moltensaltcalc
Version: 0.1.5
Summary: Molten salt simulation package employing universal machine-learned interatomic potentials (uMLIPs)
Keywords: molecular dynamics,ML potentials,ASE,materials science,atomistic simulation,interatomic potentials,machine learning interatomic potentials
Author: Daniel Isler, Lei Zhang, Max van Brenk, Suleyman Er
Author-email: Daniel Isler <isler@differ.nl>, Suleyman Er <s.er@differ.nl>
License-Expression: MIT
License-File: LICENSE
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Intended Audience :: Science/Research
Classifier: Development Status :: 3 - Alpha
Requires-Dist: ase>=3.27,<4.0
Requires-Dist: numpy>=1.26,<3.0
Requires-Dist: scipy>=1.10,<2.0
Requires-Dist: myplots>=0.1.3
Requires-Dist: tqdm
Requires-Dist: sevenn ; extra == '7net'
Requires-Dist: torch ; extra == '7net'
Requires-Dist: torch-dftd ; extra == '7net'
Requires-Dist: sevenn ; extra == '7net-nodisp'
Requires-Dist: msc-alphanet>=0.1.3 ; extra == 'alphanet'
Requires-Dist: torch==2.9.1 ; extra == 'alphanet'
Requires-Dist: torch-dftd ; extra == 'alphanet'
Requires-Dist: requests ; extra == 'alphanet'
Requires-Dist: msc-alphanet>=0.1.3 ; extra == 'alphanet-nodisp'
Requires-Dist: torch==2.9.1 ; extra == 'alphanet-nodisp'
Requires-Dist: requests ; extra == 'alphanet-nodisp'
Requires-Dist: chgnet ; extra == 'chgnet'
Requires-Dist: torch ; extra == 'chgnet'
Requires-Dist: torch-dftd ; extra == 'chgnet'
Requires-Dist: chgnet ; extra == 'chgnet-nodisp'
Requires-Dist: deepmd-kit[torch]>=3.2.0b0 ; extra == 'deepmd'
Requires-Dist: torch ; extra == 'deepmd'
Requires-Dist: torch-dftd ; extra == 'deepmd'
Requires-Dist: huggingface-hub ; extra == 'deepmd'
Requires-Dist: requests ; extra == 'deepmd'
Requires-Dist: deepmd-kit[torch]>=3.2.0b0 ; extra == 'deepmd-nodisp'
Requires-Dist: huggingface-hub ; extra == 'deepmd-nodisp'
Requires-Dist: requests ; extra == 'deepmd-nodisp'
Requires-Dist: nox ; extra == 'dev'
Requires-Dist: pre-commit ; extra == 'dev'
Requires-Dist: pytest ; extra == 'dev'
Requires-Dist: pytest-cov ; extra == 'dev'
Requires-Dist: coverage-badge ; extra == 'dev'
Requires-Dist: uv ; extra == 'dev'
Requires-Dist: dftd4 ; extra == 'dftd4'
Requires-Dist: mkdocs-material ; extra == 'docs'
Requires-Dist: mkdocstrings[python] ; extra == 'docs'
Requires-Dist: black ; extra == 'docs'
Requires-Dist: eqnorm>=0.1.1 ; extra == 'eqnorm'
Requires-Dist: torch==2.9.1 ; extra == 'eqnorm'
Requires-Dist: torch-dftd ; extra == 'eqnorm'
Requires-Dist: eqnorm>=0.1.1 ; extra == 'eqnorm-nodisp'
Requires-Dist: torch==2.9.1 ; extra == 'eqnorm-nodisp'
Requires-Dist: equflash>=0.0.2 ; python_full_version >= '3.12' and sys_platform == 'linux' and extra == 'equflash'
Requires-Dist: torch-dftd ; extra == 'equflash'
Requires-Dist: equflash>=0.0.2 ; python_full_version >= '3.12' and sys_platform == 'linux' and extra == 'equflash-nodisp'
Requires-Dist: equiformer-v2 ; extra == 'equiformer-v2'
Requires-Dist: torch ; extra == 'equiformer-v2'
Requires-Dist: torch-dftd ; extra == 'equiformer-v2'
Requires-Dist: huggingface-hub ; extra == 'equiformer-v2'
Requires-Dist: equiformer-v2 ; extra == 'equiformer-v2-nodisp'
Requires-Dist: huggingface-hub ; extra == 'equiformer-v2-nodisp'
Requires-Dist: equiformer-v3>=0.0.2 ; extra == 'equiformer-v3'
Requires-Dist: torch==2.9.1 ; extra == 'equiformer-v3'
Requires-Dist: torch-dftd ; extra == 'equiformer-v3'
Requires-Dist: huggingface-hub ; extra == 'equiformer-v3'
Requires-Dist: equiformer-v3>=0.0.2 ; extra == 'equiformer-v3-nodisp'
Requires-Dist: huggingface-hub ; extra == 'equiformer-v3-nodisp'
Requires-Dist: fairchem-core>=2.3,<3.0 ; extra == 'fairchem'
Requires-Dist: torch ; extra == 'fairchem'
Requires-Dist: torch-dftd ; extra == 'fairchem'
Requires-Dist: fairchem-core>=2.3,<3.0 ; extra == 'fairchem-nodisp'
Requires-Dist: tensorpotential ; extra == 'grace'
Requires-Dist: torch==2.9.1 ; extra == 'grace'
Requires-Dist: torch-dftd ; extra == 'grace'
Requires-Dist: tensorpotential ; extra == 'grace-nodisp'
Requires-Dist: hienet ; extra == 'hienet'
Requires-Dist: torch==2.9.1 ; extra == 'hienet'
Requires-Dist: torch-dftd ; extra == 'hienet'
Requires-Dist: hienet ; extra == 'hienet-nodisp'
Requires-Dist: torch==2.9.1 ; extra == 'hienet-nodisp'
Requires-Dist: mace-torch ; extra == 'mace'
Requires-Dist: cuequivariance ; extra == 'mace'
Requires-Dist: cuequivariance-torch ; extra == 'mace'
Requires-Dist: torch==2.9.1 ; extra == 'mace'
Requires-Dist: torch-dftd ; extra == 'mace'
Requires-Dist: mace-torch ; extra == 'mace-nodisp'
Requires-Dist: torch==2.9.1 ; extra == 'mace-nodisp'
Requires-Dist: cuequivariance ; extra == 'mace-nodisp'
Requires-Dist: cuequivariance-torch ; extra == 'mace-nodisp'
Requires-Dist: matgl[jax]>=3.0.0 ; extra == 'matgl'
Requires-Dist: torch ; extra == 'matgl'
Requires-Dist: torch-dftd ; extra == 'matgl'
Requires-Dist: matgl[jax]>=3.0.0 ; extra == 'matgl-nodisp'
Requires-Dist: matris>=0.0.1 ; extra == 'matris'
Requires-Dist: torch ; extra == 'matris'
Requires-Dist: torch-dftd ; extra == 'matris'
Requires-Dist: matris ; extra == 'matris-nodisp'
Requires-Dist: mattersim ; extra == 'mattersim'
Requires-Dist: torch==2.9.1 ; extra == 'mattersim'
Requires-Dist: torch-dftd ; extra == 'mattersim'
Requires-Dist: mattersim ; extra == 'mattersim-nodisp'
Requires-Dist: torch==2.9.1 ; extra == 'mattersim-nodisp'
Requires-Dist: nequip ; extra == 'nequip'
Requires-Dist: torch ; extra == 'nequip'
Requires-Dist: torch-dftd ; extra == 'nequip'
Requires-Dist: nequip ; extra == 'nequip-nodisp'
Requires-Dist: nequix ; extra == 'nequix'
Requires-Dist: torch==2.9.1 ; extra == 'nequix'
Requires-Dist: torch-dftd ; extra == 'nequix'
Requires-Dist: nequix ; extra == 'nequix-nodisp'
Requires-Dist: torch==2.9.1 ; extra == 'nequix-nodisp'
Requires-Dist: orb-models ; extra == 'orbitals'
Requires-Dist: torch ; extra == 'orbitals'
Requires-Dist: torch-dftd ; extra == 'orbitals'
Requires-Dist: orb-models ; extra == 'orbitals-nodisp'
Requires-Dist: tace ; extra == 'tace'
Requires-Dist: torch ; extra == 'tace'
Requires-Dist: torch-dftd ; extra == 'tace'
Requires-Dist: tace ; extra == 'tace-nodisp'
Requires-Dist: upet ; extra == 'upet'
Requires-Dist: torch ; extra == 'upet'
Requires-Dist: torch-dftd ; extra == 'upet'
Requires-Dist: upet ; extra == 'upet-nodisp'
Requires-Python: >=3.11, <3.14
Project-URL: Documentation, https://moltensaltcalc.readthedocs.io/en/latest/
Project-URL: Repository, https://github.com/leiapple/MoltenSaltCalc/
Project-URL: Homepage, https://github.com/leiapple/MoltenSaltCalc/
Project-URL: Issues, https://github.com/leiapple/MoltenSaltCalc/issues
Provides-Extra: 7net
Provides-Extra: 7net-nodisp
Provides-Extra: alphanet
Provides-Extra: alphanet-nodisp
Provides-Extra: chgnet
Provides-Extra: chgnet-nodisp
Provides-Extra: deepmd
Provides-Extra: deepmd-nodisp
Provides-Extra: dev
Provides-Extra: dftd4
Provides-Extra: docs
Provides-Extra: eqnorm
Provides-Extra: eqnorm-nodisp
Provides-Extra: equflash
Provides-Extra: equflash-nodisp
Provides-Extra: equiformer-v2
Provides-Extra: equiformer-v2-nodisp
Provides-Extra: equiformer-v3
Provides-Extra: equiformer-v3-nodisp
Provides-Extra: fairchem
Provides-Extra: fairchem-nodisp
Provides-Extra: grace
Provides-Extra: grace-nodisp
Provides-Extra: hienet
Provides-Extra: hienet-nodisp
Provides-Extra: mace
Provides-Extra: mace-nodisp
Provides-Extra: matgl
Provides-Extra: matgl-nodisp
Provides-Extra: matris
Provides-Extra: matris-nodisp
Provides-Extra: mattersim
Provides-Extra: mattersim-nodisp
Provides-Extra: nequip
Provides-Extra: nequip-nodisp
Provides-Extra: nequix
Provides-Extra: nequix-nodisp
Provides-Extra: orbitals
Provides-Extra: orbitals-nodisp
Provides-Extra: tace
Provides-Extra: tace-nodisp
Provides-Extra: upet
Provides-Extra: upet-nodisp
Description-Content-Type: text/markdown

<h4 align="center">

![License](https://img.shields.io/badge/license-MIT-blue)
![CI](https://github.com/leiapple/moltensaltcalc/actions/workflows/ci.yml/badge.svg)
![Coverage](badges/coverage.svg)

![Python version](https://img.shields.io/pypi/pyversions/moltensaltcalc)
![PyPI version](https://img.shields.io/pypi/v/moltensaltcalc?label=PyPI)
![PyPI downloads](https://img.shields.io/pypi/dm/moltensaltcalc)

</h4>

# MoltenSaltCalc

A Python package for running and analyzing molecular dynamics (MD) simulations of molten salts using machine-learned interatomic potentials (MLIPs) within the Atomic Simulation Environment (ASE).

## Authors

Daniel Isler, Lei Zhang, Max van Brenk, Süleyman Er

## Features

- System Construction: Construct molten salt systems with customizable compositions in ASE
- MLIP Integration: Support for FAIRCHEM, MACE, GRACE, ... MLIPs (other MLIPs can also be added by the user)
- Molecular Dynamics: Run NPT (constant pressure-temperature) and NVT (constant volume-temperature) simulations
- Property Analysis: Compute thermodynamic and transport properties such as density, diffusion coefficients, viscosity, and heat capacity

## Installation

Create a virtual environment and install the package with the desired MLIP backend. Each MLIP backend has separate and potentially conflicting dependencies. Therefore, only one backend should be installed per environment.

Tested on Python 3.11, 3.12, 3.13 and 3.14. All uMLIPs work on Python 3.12, but some of them do not work on the lower / higher versions. E.g. the fairchem (uma), mace and upet uMLIPs do not work with Python 3.10. On python 3.14, so far only chgnet, mattersim and upet work.

By default, the installation is shipped along with the `torch-dftd3` calculator for long-range interactions. If you do not wish to install/use the dispersion calculator at all, install with `-nodisp` instead, e.g. `pip install moltensaltcalc[mace-nodisp]`. If you want to use the (slower but more accurate) `dftd4` calculator, install the `dftd4` variant, e.g. `pip install moltensaltcalc[mace-nodisp,dftd4]`.

### GRACE

```bash
python3 -m venv .venv        # Or any other name
source .venv/bin/activate   # Linux/macOS
# or
.venv\Scripts\activate      # Windows

pip install moltensaltcalc[mace]
```

### FAIRCHEM

```bash
pip install moltensaltcalc[fairchem]
```

### MACE

```bash
pip install moltensaltcalc[mace]
```

### MatterSim

```bash
pip install moltensaltcalc[mattersim]
```

### 7net

```bash
pip install moltensaltcalc[7net]
```

### Nequip

```bash
pip install moltensaltcalc[nequip]
```

### Nequix

```bash
pip install moltensaltcalc[nequix]
```

### UPET

```bash
pip install moltensaltcalc[upet]
```

### CHGNet

```bash
pip install moltensaltcalc[chgnet]
```

### equiformer_v3

```bash
pip install moltensaltcalc[equiformer_v3]
```

### ORB-V3 (orbitals)

```bash
pip install moltensaltcalc[orbitals]
```

### TACE

```bash
pip install moltensaltcalc[tace]
```

### EquFlash

```bash
pip install moltensaltcalc[equflash]
```

### Development

If you want to contribute or make modifications to the code, lfs (to include the large `uv.lock` file) clone the repo and install in edit mode. For further details, please check our [contributing guidelines](https://github.com/leiapple/moltensaltcalc/blob/main/CONTRIBUTING.md).

```bash
git lfs clone https://github.com/leiapple/moltensaltcalc.git
cd moltensaltcalc
python3 -m venv .venv        # Or any other name
source .venv/bin/activate   # Linux/macOS
# or
.venv\Scripts\activate      # Windows
pip install -e .[dev,mace]  # Installs the selected MLIP backend and all development dependencies (pytest, etc.) in editable mode
```

To upload a changed lockfile to GitHub, run

```bash
git lfs push origin --all
```

## Usage

### Quick start

```bash
pip install moltensaltcalc[grace]
```

```python
import numpy as np

from moltensaltcalc import MoltenSaltSimulator, MoltenSaltAnalyzer

np.random.seed(42)  # Ensure reproducibility (initial random placements)

sim = MoltenSaltSimulator(
    model_name="mace",  # Use the MACE model
    model_parameters={
        "model_size": "small",  # Use the MACE-MP-0a small model
        "model_task": "Default"
    },
    dispersion=None  # Disable dispersion
)
atoms = sim.build_system(
    salt_anion=["F", "Cl"],
    salt_cation=["Na"],
    n_anions=[10, 5],  # 10 F atoms and 5 Cl atoms
    n_cations=[15],  # 15 Na atoms
    density_guess=2.0,  # g/cm³
)
sim.run_npt_simulation(
    atoms,
    T=1100,  # K
    steps=1000,  # MD steps
    timestep_fs=1.0,  # fs
    traj_file="npt_simulation.traj",  # Trajectory file
)

analyzer = MoltenSaltAnalyzer(
    traj_files_npt=["npt_simulation.traj"],  # Trajectory file(s)
    temperatures_npt=[1100],  # K
)
density = analyzer.compute_eq_density(T=1100)  # 1.68 g/cm³
C = analyzer.compute_heat_capacity(T=1100, eq_fraction=0.2)  # 0.07 J/g/K
```

### Demo

Run the example notebooks in the `demo/` directory to explore:

- system setup
- running MD simulations
- post-processing and analysis

### Workflow

The workflow of the MoltenSaltCalc aims to provide an optimized environment for molecular dynamics (MD) simulations of molten salts. A typical simulation starts by loading the MLIP backend, done in a lazy manner so the package could also be used without it (e.g. only for analysis or system setup). Next, the system is built starting out from the rocksalt structure, which is different from the usually applied random placements (which can still be used by setting the parameter `lattice` in `build_system` to `"random"`) in order to ensure the absence of clusters of ions with the same charge which typically lead to an initial volume expansion thus requiring a longer volume equilibration simulation. Since the rocksalt contains two atoms per unit cell, but we want to allow an arbitrary number of anions and cations, some random positions are removed from the larger lattice to match the desired composition. The volume of the resulting system is adjusted to match the desired density guess (input variable `density_guess` in g/cm<sup>3</sup>).

Before starting the MD simulation, the velocities are initialized with a Maxwell-Boltzmann distribution according to the desired temperature, while keeping the center of mass and the overall rotation fixed to ensure the temperature is not under-shot because the whole system is moving. Starting out from this, first an NPT (constant particles, pressure, temperature) simulation is run to equilibrate the system volume and obtain the density and thermal expansion of the molten salt (`MoltenSaltAnalyzer`). Then an NVT (constant particles, volume, temperature) simulation is run to obtain more properties such as diffusion, viscosity or heat capacity of the molten salt (`MoltenSaltAnalyzer`). The workflow is illustrated below:

![Workflow](imgs/workflow_diagram.png)

## Project Structure

```
moltensaltcalc/
├── .github/workflows/ci.yml# CI workflow
├── badges/coverage.svg     # Coverage badge
├── demo/
│   ├── simulator.ipynb     # Demo notebook for the simulator
│   ├── analyzer.ipynb      # Demo notebook for the analyzer
|   └── demo_simulation_results/ # Example trajectory used by the demo
├── docs/...                # Documentation
├── src/moltensaltcalc/     # Source code
│   ├── __init__.py         # Package exports and available models
│   ├── simulator.py        # MoltenSaltSimulator class
│   ├── analyzer.py         # MoltenSaltAnalyzer class
│   ├── model_discovery.py  # Discovery of available MLIPs
│   ├── model_errors.py     # Error formatting
│   ├── registry.py         # MLIP model registration
|   └── models/             # MLIP model implementations
|       ├── __init__.py
|       ├── 7net.py
|       ├── alphanet.py
|       ├── chgnet.py
|       ├── deepmd.py
|       ├── eqnorm.py
|       ├── equflash.py
|       ├── equiformer_v3.py
|       ├── fairchem.py
|       ├── grace.py
|       ├── hienet.py
|       ├── mace.py
|       ├── matgl.py
|       ├── matris.py
|       ├── mattersim.py
|       ├── nequip.py
|       ├── nequix.py
|       ├── orbitals.py
|       ├── tace.py
|       ├── upet.py
|       └── vasp.py
├── tests/                  # PyTests
│   ├── __init__.py
│   ├── test_simulator.py   # Tests for the simulator using the GRACE uMLIP
│   ├── test_analyzer.py    # Tests for the analyzer using the stored trajectories
|   ├── test_uMLIPs.py      # Tests for the different uMLIP backends
│   ├── test_analyzer_trajectories/  # Example trajectories used by the tests
|   └── test_uMLIP_precompiled/  # Precompiled models used by the tests
├── .gitattributes
├── .gitignore              # Gitignore file: Python template + some custom rules at the end
├── .readthedocs.yaml       # ReadTheDocs configuration
├── .pre-commit-config.yaml # Pre-commit configuration
├── CITATION.cff            # Citation file
├── CONTRIBUTING.md         # Contributing guidelines
├── LICENSE                 # License file
├── mkdocs.yml              # MkDocs configuration
├── noxfile.py              # Nox configuration for uMLIP testing in different environments
├── pyproject.toml          # Build configuration
├── README.md               # This file
└── uv.lock                 # Uv lock file (stored in git lfs, get with `git lfs clone`)
```

## License

This project is licensed under the MIT License, see the [LICENSE](https://github.com/leiapple/MoltenSaltCalc/blob/main/LICENSE) file for details.

## Support

For questions, bug reports, or feature requests, please open an issue on [GitHub](https://github.com/leiapple/MoltenSaltCalc/issues).
