Open In Colab

Molecular Dynamics

Set up environment (optional)

These steps are required for Google Colab, but may work on other systems too:

[1]:
# import locale
# locale.getpreferredencoding = lambda: "UTF-8"

# ! pip uninstall torch torchaudio torchvision transformers numpy -y
# ! uv pip install janus-core[all] data-tutorials torch==2.5.1 --system
# get_ipython().kernel.do_shutdown(restart=True)
[2]:
from ase.build import bulk
from ase.io import read, write
from weas_widget import WeasWidget
from janus_core.helpers.stats import Stats
from janus_core.processing import post_process
import yaml

import numpy as np
import matplotlib.pyplot as plt

NVT

In janus_core we can simulate MD in various ensembles. To start we will look at a simple NVT simulation of NaCl.

Using the ASE we can build an NaCl crystal sample. Which we can look at using Weas.

[3]:
NaCl = bulk("NaCl", "rocksalt", a=5.63, cubic=True)
NaCl = NaCl * (2, 2, 2)
write("../data/NaCl.xyz", NaCl)

v=WeasWidget()
v.from_ase(NaCl)
v
[3]:

Running the simulation

For our simulation we can prepare a config YAML file for the janus md command.

Here we set the ensemble and the structure.

The following 2 options specify the simulation steps to perform in MD and the MD temperature in K. The default timestep is 1 fs.

The final 2 options set the frequency to write statistics and trajectory frames.

If you have a GPU on you, then the commented out option will speed things up considerably.

[4]:
%%writefile config-nvt.yml

arch: mace_mp
ensemble: nvt
struct: ../data/NaCl.xyz

steps: 250
temp: 100

stats_every: 10
traj_every: 10
restart_every: 50

tracker: False

# If you have an Nvidia GPU.
#device: cuda
Writing config-nvt.yml

As with other janus_core functionalities the help method will show the available options along with units

[5]:
!janus md --help
                                                                                
 Usage: janus md [OPTIONS]                                                      
                                                                                
 Run molecular dynamics simulation, and save trajectory and statistics.


╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --config        TEXT  Path to configuration file.                            │
│ --help                Show this message and exit.                            │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ MLIP calculator ────────────────────────────────────────────────────────────╮
│ *  --arch               [mace|mace_mp|mace_off|m  MLIP architecture to use   │
│                         3gnet|chgnet|alignn|seve  for calculations.          │
│                         nnet|nequip|dpa3|orb|mat  [required]                 │
│                         tersim|grace|esen|equifo                             │
│                         rmer]                                                │
│    --device             [cpu|cuda|mps|xpu]        Device to run calculations │
│                                                   on.                        │
│                                                   [default: cpu]             │
│    --model              TEXT                      MLIP model name, or path   │
│                                                   to model.                  │
│                                                   [default: None]            │
│    --model-path         TEXT                      Deprecated. Please use     │
│                                                   --model                    │
│                                                   [default: None]            │
│    --calc-kwargs        DICT                      Keyword arguments to pass  │
│                                                   to selected calculator.    │
│                                                   Must be passed as a        │
│                                                   dictionary wrapped in      │
│                                                   quotes, e.g. "{'key':      │
│                                                   value}".                   │
│                                                   [default: None]            │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Calculation ────────────────────────────────────────────────────────────────╮
│ *  --ensemble                             [nph|npt|nve|nv  Name of           │
│                                           t|nvt-nh|nvt-cs  thermodynamic     │
│                                           vr|npt-mtk]      ensemble.         │
│                                                            [required]        │
│ *  --struct                               PATH             Path of structure │
│                                                            to simulate.      │
│                                                            [required]        │
│    --steps                                INTEGER          Number of steps   │
│                                                            in MD simulation. │
│                                                            [default: 0]      │
│    --timestep                             FLOAT            Timestep for      │
│                                                            integrator, in    │
│                                                            fs.               │
│                                                            [default: 1.0]    │
│    --temp                                 FLOAT            Temperature, in   │
│                                                            K.                │
│                                                            [default: 300.0]  │
│    --equil-steps                          INTEGER          Maximum number of │
│                                                            steps at which to │
│                                                            perform           │
│                                                            optimization and  │
│                                                            reset             │
│                                                            [default: 0]      │
│    --minimize         --no-minimize                        Whether to        │
│                                                            minimize          │
│                                                            structure during  │
│                                                            equilibration.    │
│                                                            [default:         │
│                                                            no-minimize]      │
│    --minimize-eve…                        INTEGER          Frequency of      │
│                                                            minimizations.    │
│                                                            Default disables  │
│                                                            minimization      │
│                                                            after beginning   │
│                                                            dynamics.         │
│                                                            [default: -1]     │
│    --minimize-kwa…                        DICT             Keyword arguments │
│                                                            to pass to        │
│                                                            optimizer. Must   │
│                                                            be passed as a    │
│                                                            dictionary        │
│                                                            wrapped in        │
│                                                            quotes, e.g.      │
│                                                            "{'key': value}". │
│                                                            [default: None]   │
│    --rescale-velo…    --no-rescale-ve…                     Whether to        │
│                                                            rescale           │
│                                                            velocities during │
│                                                            equilibration.    │
│                                                            [default:         │
│                                                            no-rescale-veloc… │
│    --remove-rot       --no-remove-rot                      Whether to remove │
│                                                            rotation during   │
│                                                            equilibration.    │
│                                                            [default:         │
│                                                            no-remove-rot]    │
│    --rescale-every                        INTEGER          Frequency to      │
│                                                            rescale           │
│                                                            velocities during │
│                                                            equilibration.    │
│                                                            [default: 10]     │
│    --post-process…                        DICT             Keyword arguments │
│                                                            to pass to        │
│                                                            post-processer.   │
│                                                            Must be passed as │
│                                                            a dictionary      │
│                                                            wrapped in        │
│                                                            quotes, e.g.      │
│                                                            "{'key': value}". │
│                                                            [default: None]   │
│    --correlation-…                        DICT             Keyword arguments │
│                                                            to pass to md for │
│                                                            on-the-fly        │
│                                                            correlations.     │
│                                                            Must be passed as │
│                                                            a list of         │
│                                                            dictionaries      │
│                                                            wrapped in        │
│                                                            quotes, e.g.      │
│                                                            "[{'key' :        │
│                                                            values}]".        │
│                                                            [default: None]   │
│    --seed                                 INTEGER          Random seed for   │
│                                                            numpy.random and  │
│                                                            random functions. │
│                                                            [default: None]   │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Ensemble configuration ─────────────────────────────────────────────────────╮
│ --thermostat-time            FLOAT    Thermostat time for NPT, NPT-MTK or    │
│                                       NVT Nosé-Hoover simulation, in fs.     │
│                                       Default is 50 fs for NPT and NVT       │
│                                       Nosé-Hoover, or 100 fs for NPT-MTK.    │
│                                       [default: None]                        │
│ --barostat-time              FLOAT    Barostat time for NPT, NPT-MTK or NPH  │
│                                       simulation, in fs. Default is 75 fs    │
│                                       for NPT and NPH, or 1000 fs for        │
│                                       NPT-MTK.                               │
│                                       [default: None]                        │
│ --bulk-modulus               FLOAT    Bulk modulus for NPT or NPH            │
│                                       simulation, in GPa.                    │
│                                       [default: 2.0]                         │
│ --pressure                   FLOAT    Pressure for NPT or NPH simulation, in │
│                                       GPa.                                   │
│                                       [default: 0.0]                         │
│ --friction                   FLOAT    Friction coefficient for NVT           │
│                                       simulation, in fs^-1.                  │
│                                       [default: 0.005]                       │
│ --taut                       FLOAT    Temperature coupling time constant for │
│                                       NVT CSVR simulation, in fs.            │
│                                       [default: 100.0]                       │
│ --thermostat-chain           INTEGER  Number of variables in thermostat      │
│                                       chain for NPT MTK simulation.          │
│                                       [default: 3]                           │
│ --barostat-chain             INTEGER  Number of variables in barostat chain  │
│                                       for NPT MTK simulation.                │
│                                       [default: 3]                           │
│ --thermostat-substeps        INTEGER  Number of sub-steps in thermostat      │
│                                       integration for NPT MTK simulation.    │
│                                       [default: 1]                           │
│ --barostat-substeps          INTEGER  Number of sub-steps in barostat        │
│                                       integration for NPT MTK simulation.    │
│                                       [default: 1]                           │
│ --ensemble-kwargs            DICT     Keyword arguments to pass to ensemble  │
│                                       initialization. Must be passed as a    │
│                                       dictionary wrapped in quotes, e.g.     │
│                                       "{'key': value}".                      │
│                                       [default: None]                        │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Heating/cooling ramp ───────────────────────────────────────────────────────╮
│ --temp-start        FLOAT  Temperature to start heating, in K.               │
│                            [default: None]                                   │
│ --temp-end          FLOAT  Maximum temperature for heating, in K.            │
│                            [default: None]                                   │
│ --temp-step         FLOAT  Size of temperature steps when heating, in K.     │
│                            [default: None]                                   │
│ --temp-time         FLOAT  Time between heating steps, in fs.                │
│                            [default: None]                                   │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Restart settings ───────────────────────────────────────────────────────────╮
│ --restart             --no-restart                    Whether restarting     │
│                                                       dynamics.              │
│                                                       [default: no-restart]  │
│ --restart-auto        --no-restart-auto               Whether to infer       │
│                                                       restart file if        │
│                                                       restarting dynamics.   │
│                                                       [default:              │
│                                                       restart-auto]          │
│ --restart-stem                               PATH     Stem for restart file  │
│                                                       name. Default inferred │
│                                                       from `file_prefix`.    │
│                                                       [default: None]        │
│ --restart-every                              INTEGER  Frequency of steps to  │
│                                                       save restart info.     │
│                                                       [default: 1000]        │
│ --rotate-restart      --no-rotate-restart             Whether to rotate      │
│                                                       restart files.         │
│                                                       [default:              │
│                                                       no-rotate-restart]     │
│ --restarts-to-keep                           INTEGER  Restart files to keep  │
│                                                       if rotating.           │
│                                                       [default: 4]           │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Output files ───────────────────────────────────────────────────────────────╮
│ --final-file                         PATH     File to save final             │
│                                               configuration at each          │
│                                               temperature of similation.     │
│                                               Default inferred from          │
│                                               `file_prefix`.                 │
│                                               [default: None]                │
│ --stats-file                         PATH     File to save thermodynamical   │
│                                               statistics. Default inferred   │
│                                               from `file_prefix`.            │
│                                               [default: None]                │
│ --stats-every                        INTEGER  Frequency to output            │
│                                               statistics.                    │
│                                               [default: 100]                 │
│ --traj-file                          PATH     File to save trajectory.       │
│                                               Default inferred from          │
│                                               `file_prefix`.                 │
│                                               [default: None]                │
│ --traj-append    --no-traj-append             Whether to append trajectory.  │
│                                               [default: no-traj-append]      │
│ --traj-start                         INTEGER  Step to start saving           │
│                                               trajectory.                    │
│                                               [default: 0]                   │
│ --traj-every                         INTEGER  Frequency of steps to save     │
│                                               trajectory.                    │
│                                               [default: 100]                 │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Structure I/O ──────────────────────────────────────────────────────────────╮
│ --file-prefix         PATH  Prefix for output files, including directories.  │
│                             Default directory is ./janus_results, and        │
│                             default filename prefix is inferred from the     │
│                             input stucture filename.                         │
│                             [default: None]                                  │
│ --read-kwargs         DICT  Keyword arguments to pass to ase.io.read. Must   │
│                             be passed as a dictionary wrapped in quotes,     │
│                             e.g. "{'key': value}". By default,               │
│                             read_kwargs['index'] = -1, so only the last      │
│                             structure is read.                               │
│                             [default: None]                                  │
│ --write-kwargs        DICT  Keyword arguments to pass to ase.io.write when   │
│                             saving any structures. Must be passed as a       │
│                             dictionary wrapped in quotes, e.g. "{'key':      │
│                             value}".                                         │
│                             [default: None]                                  │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Logging/summary ────────────────────────────────────────────────────────────╮
│ --log                                         PATH     Path to save logs to. │
│                                                        Default is inferred   │
│                                                        from `file_prefix`    │
│                                                        [default: None]       │
│ --tracker                --no-tracker                  Whether to save       │
│                                                        carbon emissions of   │
│                                                        calculation           │
│                                                        [default: tracker]    │
│ --summary                                     PATH     Path to save summary  │
│                                                        of inputs, start/end  │
│                                                        time, and carbon      │
│                                                        emissions. Default is │
│                                                        inferred from         │
│                                                        `file_prefix`.        │
│                                                        [default: None]       │
│ --progress-bar           --no-progress-bar             Whether to show       │
│                                                        progress bar.         │
│                                                        [default:             │
│                                                        progress-bar]         │
│ --update-progress-ev…                         INTEGER  Number of timesteps   │
│                                                        between progress bar  │
│                                                        updates. Default is   │
│                                                        steps / 100, rounded  │
│                                                        up.                   │
│                                                        [default: None]       │
╰──────────────────────────────────────────────────────────────────────────────╯

We can begin our simulation by calling the following shell command. Which may take a few minutes on a CPU.

[6]:
! janus md --config config-nvt.yml
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/e3nn/o3/_wigner.py:10: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  _Jd, _W3j_flat, _W3j_indices = torch.load(os.path.join(os.path.dirname(__file__), 'constants.pt'))
cuequivariance or cuequivariance_torch is not available. Cuequivariance acceleration will be disabled.
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:143: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
Using head Default out of ['Default']
Simulating at 100.0 K... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 250/250 0:00:00

The outputs

While we are waiting for the simulation we can familiarise ourselves with the output files you will see.

The directory janus_results will be populated with the following key files named NaCl-nvt-T100.0-

  • stats.dat

  • traj.extxyz

  • final.extxyz

  • md-log.yml

  • md-summary.yml

The stats file contains core statistical data on the simulation, including the step, wall-time, temperature, volume, and pressure tensor.

You could view this now while the simulation is running by the shell command

$ head janus_results/NaCl-nvt-T100.0-stats.dat

Giving something like the following

# Step | Real_Time [s] | Time [fs] | Epot/N [eV] ...
         0 1.044          0.00 -3.37876019e+00 ...
        10 8.772         10.00 -3.37789444e+00 ...

The first line in the stats files also includes the units used. Temperatures are all in K, lengths in Angstroms, and Pressures in GPa.

The traj and final files including the MD trajectory and the final configuration in EXTXYZ format.

md-log and md-summary give information about the running of the simulation and a summary of the options used in janus-md respectively. The latter in particular is worth a look as it also includes the default values for example the bulk_modulus.

$ cat janus_results/NaCl-nvt-T100.0-md-summary.yml
command: janus md
config:
  arch: mace_mp
  barostat_chain: 3
  barostat_substeps: 1
  barostat_time: null
  bulk_modulus: 2.0
  calc_kwargs: {}
...

These output files provide a full record of what you have done alongside the standard MD outputs.

Analysing the simulation

Once the simulation has completed we can make use of the utilities in janus_core. In particular janus_core.helpers.stats.Stats which makes viewing the stats.dat file straightforward (alternatively the file is readable by np.loadtxt).

Passing the path to our statistics we can observe the temperature, volume, and pressure resulting from the potential in NVT.

[7]:
stats = Stats("janus_results/NaCl-nvt-T100.0-stats.dat")
fig, ax = plt.subplots(ncols=3, figsize=(10,3))
ax[0].plot(stats[0], stats[5])
ax[0].set_xlabel("Step")
ax[0].set_ylabel("Temperature")

ax[1].plot(stats[0], stats[8])
ax[1].set_xlabel("Step")
ax[1].set_ylabel("Volume")

ax[2].plot(stats[0], stats[9])
ax[2].set_xlabel("Step")
ax[2].set_ylabel("Pressure")
plt.tight_layout()
../../_images/tutorials_cli_md_16_0.png

Since we have been saving the trajectory every 10 steps we can also see a nice movie of our computers labour.

[8]:
traj = read("janus_results/NaCl-nvt-T100.0-traj.extxyz", index=":")
v=WeasWidget()
v.from_ase(traj)
v.avr.model_style = 1
v.avr.show_hydrogen_bonds = True
v
[8]:

Another helper provided in janus_core is the post processing module from janus_core.processing.post_process. This enables velocity auto-correlation function and radial distribution function computation from trajectory files.

Using our data we can take a look at the RDF, and VAF

[9]:
rdf = post_process.compute_rdf(traj, rmax=5.0, nbins=50)
vaf = post_process.compute_vaf(traj)

fig, ax = plt.subplots(ncols=2, figsize=(8,4))

ax[0].plot(rdf[0], rdf[1])
ax[0].set_ylabel("RDF")
ax[0].set_xlabel("Distance / Å")

ax[1].plot(vaf[0]*10, vaf[1][0]/vaf[1][0][0])
ax[1].set_ylabel("VAF")
ax[1].set_xlabel("Lag time, fs")

plt.tight_layout()
../../_images/tutorials_cli_md_20_0.png

Continuing the simulation

Using the final configuration we can continue the simulation easily. We can make use of our old config but override some of the values manually.

Here we indicate we want to restart with --restart, and pass our parameters from before via --config. janus_core will find our old files and begin dynamics again from the last restart file. For us this happens to be at step 250. Because we override the steps with --steps 300 we get an additional 50 simulation steps appended to our statistics and trajectory files.

[10]:
! janus md --restart --steps 300 --config config-nvt.yml
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/e3nn/o3/_wigner.py:10: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  _Jd, _W3j_flat, _W3j_indices = torch.load(os.path.join(os.path.dirname(__file__), 'constants.pt'))
cuequivariance or cuequivariance_torch is not available. Cuequivariance acceleration will be disabled.
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:143: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
Using head Default out of ['Default']
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
Using head Default out of ['Default']
Simulating at 100.0 K... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 298/300 0:00:01

[11]:
stats = Stats("janus_results/NaCl-nvt-T100.0-stats.dat")
fig, ax = plt.subplots()
ax.plot(stats[0], stats[5])
ax.set_xlabel("Step")
ax.set_ylabel("Temperature")
[11]:
Text(0, 0.5, 'Temperature')
../../_images/tutorials_cli_md_23_1.png

Correlations at runtime

Alongside post-processing of trajectory files janus_core includes a correlations module to calculate correlation functions at runtime that do not require trajectory storage.

For example beginning again from our initial structure we can compute the VAF online with the addition of correlation_kwargs.

In these options we specify a list of correlations \(\langle a b \rangle\) to compute. First the VAF by requesting a correlation with Velocity as the quantity \(a\) (\(b\) is assumed to be the same if not set) up to \(250\) correlation points (lag times). Additionally we also create a stress auto-correlation function by forming a second correlation with Stress. In this we also set the stress components to be correlated over, again \(b\) left unspecified means it is the same as a. These extra settings will give us the correlation function \(\frac{1}{3}(\langle \sigma_{xy}\sigma_{xy}\rangle+\langle \sigma_{yz}\sigma_{yz}\rangle+\langle \sigma_{zx}\sigma_{zx}\rangle)\).

By default a correlation is updated every step. This can be controlled by the option update_frequency.

[12]:
%%writefile config-nvt-cor.yml

ensemble: nvt
struct: ../data/NaCl.xyz
arch: mace_mp

steps: 250
temp: 100

stats_every: 10
traj_every: 250

correlation_kwargs:
  vaf:
    a: Velocity
    points: 250
  saf:
    a: Stress
    a_kwargs: {'components': ['xy', 'yz', 'zx']}
    points: 250

tracker: False

# If you have an Nvidia GPU.
#device: cuda
Writing config-nvt-cor.yml
[13]:
! janus md --config config-nvt-cor.yml
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/e3nn/o3/_wigner.py:10: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  _Jd, _W3j_flat, _W3j_indices = torch.load(os.path.join(os.path.dirname(__file__), 'constants.pt'))
cuequivariance or cuequivariance_torch is not available. Cuequivariance acceleration will be disabled.
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:143: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
Using head Default out of ['Default']
Simulating at 100.0 K... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 250/250 0:00:00

Analysing correlations

We can load up the new file NaCl-nvt-T100.0-cor.dat which contains the correlation function we asked for in a YAML format.

Previously we computed the VAF using the trajectory saved every 10 frames, our result here will be more accurate at the expense of a longer runtime, but we do not require any trajectory data.

[14]:
with open("janus_results/NaCl-nvt-T100.0-cor.dat", "r") as cor:
    data = yaml.safe_load(cor)
    vaf = data["vaf"]
    saf = data["saf"]

fig, ax = plt.subplots(ncols=2, figsize=(8,4))

ax[0].plot(vaf['lags'], np.array(vaf['value'])/vaf['value'][0], label="VAF every step (online)")
ax[0].set_ylabel("VAF")
ax[0].set_xlabel("Lag time, fs")

vaf_post = post_process.compute_vaf(traj)

ax[0].plot(vaf_post[0]*10, vaf_post[1][0]/vaf_post[1][0][0], label="VAF every 10 steps (post)")
ax[0].set_ylabel("VAF")
ax[0].set_xlabel("Lag time, fs")

ax[0].legend()

ax[1].plot(saf['lags'], np.array(saf['value'])/saf['value'][0])
ax[1].set_ylabel("SAF")
ax[1].set_xlabel("Lag time, fs")

plt.tight_layout()
../../_images/tutorials_cli_md_28_0.png

Heating

janus_core has a facility for heating or cooling during MD. This is controlled by 4 options.

  • temp_start: the starting temperature in Kelvin.

  • temp_end: the final temperature in Kelvin.

  • temp_step: the temperature increment in Kelvin.

  • temp_time: the number of time steps at each temperature increment.

In this context the options temp and steps will apply in a normal MD run after the heating has finished.

We can make use of this to perform a naive estimation of melting in NPT by progressively increasing temperature. To keep the cell angles fixed we also apply a mask via ``ensemble_kwargs``` which are passed to ASE’s thermo-/baro-stats.

We are going to cheat by using a pre-prepared structure NaCl-1070.extxyz. This was equilibrated from ../data/NaCl.xyz in a similar way that we are about to continue but over many more steps. We can have a quick look at our starting point.

[15]:
v=WeasWidget()
structure = read("../data/NaCl-1040.extxyz")
structure.wrap()
v.from_ase(structure)
v
[15]:

We add in our heating parameters and npt options in a new config and run it.

[16]:
%%writefile config-npt-heat.yml

ensemble: npt
ensemble_kwargs:
  mask: [[1,0,0],[0,1,0],[0,0,1]]

struct: ../data/NaCl-1040.extxyz
arch: mace_mp

temp_start: 1040
temp_end: 1080
temp_step: 5
temp_time: 25

stats_every: 10
traj_every: 10

tracker: False

# If you have an Nvidia GPU.
#device: cuda
Writing config-npt-heat.yml
[17]:
! janus md --config config-npt-heat.yml
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/e3nn/o3/_wigner.py:10: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  _Jd, _W3j_flat, _W3j_indices = torch.load(os.path.join(os.path.dirname(__file__), 'constants.pt'))
cuequivariance or cuequivariance_torch is not available. Cuequivariance acceleration will be disabled.
Using Materials Project MACE for MACECalculator with /home/runner/.cache/mace/20231210mace128L0_energy_epoch249model
Using float64 for MACECalculator, which is slower but more accurate. Recommended for geometry optimization.
/home/runner/work/janus-core/janus-core/.venv/lib/python3.12/site-packages/mace/calculators/mace.py:143: UserWarning: Environment variable TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD detected, since the`weights_only` argument was not explicitly passed to `torch.load`, forcing weights_only=False.
  torch.load(f=model_path, map_location=device)
Using head Default out of ['Default']
Simulating at 1080.0 K... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 225/225 0:00:00

[18]:
v=WeasWidget()
structure = read("janus_results/NaCl-1040-npt-T1040.0-T1080.0-p0.0-final.extxyz")
structure.wrap()
v.from_ase(structure)
v
[18]:

Extensions

Viscosity

Simulate liquid NaCl in NVE at about 1080 K and calculate the SAF as we have done before. Use the SAF to estimate the viscosity \(\eta = \frac{V}{3k_{b}T}\int \langle \sigma_{xy}\sigma_{xy}\rangle+ \langle \sigma_{yz}\sigma_{yz}\rangle+ \langle \sigma_{zx}\sigma_{zx}\rangle dt\). Experimentally the viscosity is \(1.030\) mN s m \({}^{-2}\) [1]. Long simulations and repeats are required for accuracy, but try 1000 fs.

You will find the pre-equilibrated structure NaCl-1040.xyz useful!

[1] Janz, G.J., 1980. Molten salts data as reference standards for density, surface tension, viscosity, and electrical conductance: KNO3 and NaCl. Journal of physical and chemical reference data, 9(4), pp.791-830.

Melting

Try heating the NaCl crystal ../data/NaCl.xyz up to 1080 K using the temperature ramp options in NPT. To keep cell angles constant, you will want to add the options

ensemble_kwargs:
  mask: [[1,0,0],[0,1,0],[0,0,1]]

temp_time: 2000 would be good if you have a GPU. You can also “cheat” by staring from NaCl-1000.xyz

Experimentally NaCl melts at about 1074 Kelvin at atmospheric pressure.