-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
192 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
"""Call LAMMPS to get the forces and energy in a given configuration.""" | ||
import os | ||
from pathlib import Path | ||
from typing import Dict, Tuple | ||
|
||
import lammps | ||
import numpy as np | ||
import pandas as pd | ||
import yaml | ||
from pymatgen.core import Element | ||
|
||
from crystal_diffusion import DATA_DIR | ||
|
||
|
||
def get_energy_and_forces_from_lammps(positions: np.ndarray, | ||
box: np.ndarray, | ||
atom_types: np.ndarray, | ||
atom_type_map: Dict[int, str] = {1: 'Si'}, | ||
tmp_work_dir: str = './', | ||
pair_coeff_dir: Path = DATA_DIR) -> Tuple[float, pd.DataFrame]: | ||
"""Call LAMMPS to compute the forces on all atoms in a configuration. | ||
Args: | ||
positions: atomic positions as a n_atom x spatial dimension array | ||
box: spatial dimension x spatial dimension array representing the periodic box. Assumed to be orthogonal. | ||
atom_types: n_atom array with an index representing the type of each atom | ||
atom_type_map (optional): map from index representing an atom type to a description of the atom. | ||
Defaults to {1: "Si"} | ||
tmp_work_dir (optional): directory where the LAMMPS output will be written (and deleted). Defaults to ./ | ||
pair_coeff_dir (optional): directory where the potentials as .sw files are stored. Defaults to DATA_DIR | ||
Returns: | ||
energy | ||
dataframe with x, y, z coordinates and fx, fy, fz information in a dataframe. | ||
""" | ||
n_atom = positions.shape[0] | ||
assert atom_types.shape == (n_atom, ), f"Atom types should match the number of atoms. Got {atom_types.shape}." | ||
lmp = lammps.lammps() # create a lammps run | ||
assert np.allclose(box, np.diag(np.diag(box))), "only orthogonal LAMMPS box are valid" | ||
lmp.command(f"region simbox block 0 {box[0, 0]} 0 {box[1, 1]} 0 {box[2, 2]}") # TODO what if box is not orthogonal | ||
lmp.command("create_box 1 simbox") | ||
lmp.command("pair_style sw") | ||
for k, v in atom_type_map.items(): | ||
elem = Element(v) | ||
lmp.command(f"mass {k} {elem.atomic_mass.real}") # the .real is to get the value without the unit | ||
lmp.command(f"group {v} type {k}") | ||
lmp.command(f"pair_coeff * * {os.path.join(pair_coeff_dir, f'{v.lower()}.sw')} {v}") | ||
for i in range(n_atom): | ||
lmp.command(f"create_atoms {atom_types[i]} single {' '.join(map(str, positions[i, :]))}") | ||
lmp.command("fix 1 all nvt temp 300 300 0.01") # selections here do not matter because we only do 1 step | ||
lmp.command(f"dump 1 all yaml 1 {os.path.join(tmp_work_dir, 'dump.yaml')} id x y z fx fy fz") # TODO not good in 2D | ||
lmp.command("run 0") # 0 is the last step index - so run 0 means no MD update - just get the initial forces | ||
|
||
# read informations from lammps output | ||
with open(os.path.join(tmp_work_dir, "dump.yaml"), "r") as f: | ||
dump_yaml = yaml.safe_load_all(f) | ||
doc = next(iter(dump_yaml)) | ||
os.remove(os.path.join(tmp_work_dir, "dump.yaml")) # no need to keep the output as artefact | ||
forces = pd.DataFrame(doc['data'], columns=doc['keywords']).sort_values("id") # organize in a dataframe | ||
|
||
# get the energy | ||
ke = lmp.get_thermo('ke') # kinetic energy - should be 0 as atoms are created with 0 velocity | ||
pe = lmp.get_thermo('pe') # potential energy | ||
energy = ke + pe | ||
|
||
return energy, forces |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/bin/bash | ||
|
||
TEMPERATURE=300 | ||
BOX_SIZE=2 | ||
STEP=10000 | ||
CROP=10000 | ||
NTRAIN_RUN=10 | ||
NVALID_RUN=5 | ||
|
||
NRUN=$(($NTRAIN_RUN + $NVALID_RUN)) | ||
|
||
for SEED in $(seq 2 $NRUN); | ||
do | ||
if [ "$SEED" -le $NTRAIN_RUN ]; then | ||
MODE="train" | ||
else | ||
MODE="valid" | ||
fi | ||
echo $MODE $SEED | ||
mkdir -p "${MODE}_run_${SEED}" | ||
cd "${MODE}_run_${SEED}" | ||
lmp < ../in.si.lammps -v STEP $(($STEP + $CROP)) -v T $TEMPERATURE -v S $BOX_SIZE -v SEED $SEED | ||
|
||
# extract the thermodynamic outputs in a yaml file | ||
egrep '^(keywords:|data:$|---$|\.\.\.$| - \[)' log.lammps > thermo_log.yaml | ||
|
||
mkdir -p "uncropped_outputs" | ||
mv "dump.si-${TEMPERATURE}-${BOX_SIZE}.yaml" uncropped_outputs/ | ||
mv thermo_log.yaml uncropped_outputs/ | ||
|
||
tail -n 760076 uncropped_outputs/dump.si-300-2.yaml > lammps_dump.yaml | ||
|
||
python ../../crop_lammps_outputs.py \ | ||
--lammps_yaml "uncropped_outputs/dump.si-${TEMPERATURE}-${BOX_SIZE}.yaml" \ | ||
--lammps_thermo "uncropped_outputs/thermo_log.yaml" \ | ||
--crop $CROP \ | ||
--output_dir ./ | ||
|
||
cd .. | ||
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
log log.lammps | ||
|
||
units metal | ||
atom_style atomic | ||
atom_modify map array | ||
|
||
lattice diamond 5.43 | ||
region simbox block 0 ${S} 0 ${S} 0 ${S} | ||
create_box 1 simbox | ||
create_atoms 1 region simbox | ||
|
||
mass 1 28.0855 | ||
|
||
group Si type 1 | ||
|
||
pair_style sw | ||
pair_coeff * * ../../si.sw Si | ||
|
||
velocity all create ${T} ${SEED} | ||
|
||
dump 1 all yaml 1 dump.si-${T}-${S}.yaml id type x y z fx fy fz | ||
|
||
thermo_style yaml | ||
thermo 1 | ||
#==========================Output files======================== | ||
|
||
fix 1 all nvt temp ${T} ${T} 0.01 | ||
run ${STEP} | ||
unfix 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import numpy as np | ||
import pytest | ||
|
||
from crystal_diffusion.oracle.lammps import get_energy_and_forces_from_lammps | ||
|
||
|
||
@pytest.fixture | ||
def high_symmetry_lattice(): | ||
box = np.eye(3) * 4 | ||
return box | ||
|
||
|
||
@pytest.fixture | ||
def high_symmetry_positions(): | ||
positions = np.array([[0, 0, 0], [2, 2, 2]]) | ||
return positions | ||
|
||
|
||
# do not run on github because no lammps | ||
@pytest.mark.not_on_github | ||
def test_high_symmetry(high_symmetry_positions, high_symmetry_lattice): | ||
energy, forces = get_energy_and_forces_from_lammps(high_symmetry_positions, high_symmetry_lattice, | ||
atom_types=np.array([1, 1])) | ||
for x in ['x', 'y', 'z']: | ||
assert np.allclose(forces[f'f{x}'], [0, 0]) | ||
assert energy < 0 | ||
|
||
|
||
@pytest.fixture | ||
def low_symmetry_positions(): | ||
positions = np.array([[0.23, 1.2, 2.01], [3.2, 0.9, 3.87]]) | ||
return positions | ||
|
||
|
||
@pytest.mark.not_on_github | ||
def test_low_symmetry(low_symmetry_positions, high_symmetry_lattice): | ||
energy, forces = get_energy_and_forces_from_lammps(low_symmetry_positions, high_symmetry_lattice, | ||
atom_types=np.array([1, 1])) | ||
for x in ['x', 'y', 'z']: | ||
assert not np.allclose(forces[f'f{x}'], [0, 0]) | ||
assert energy < 0 |