Skip to content

Commit 9a9745a

Browse files
Merge pull request #5 from openkim-hackathons/ilia-updates-May2026
Ilia updates may2026
2 parents 1c1d379 + 796ece1 commit 9a9745a

15 files changed

Lines changed: 2166 additions & 1893 deletions

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
output export-ignore
22
local-props export-ignore
33
run.py export-ignore
4+
job.txt export-ignore

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
output/*
1+
output*
2+
log.*
3+
*.log
4+
*.out
25

36
# Byte-compiled / optimized / DLL files
47
__pycache__/

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This test driver repeats the unit cell to build a supercell and then runs a mole
88
NPT ensemble using Lammps.
99

1010
This test driver uses kim-convergence to detect an equilibrated molecular-dynamics simulation. It checks
11-
convergence of the volume, temperature, enthalpy and cell shape parameters every 10000 timesteps.
11+
convergence of the volume, temperature, enthalpy and cell shape parameters every 10000 timesteps (default).
1212

1313
During the equilibrated part of the simulation, the test driver averages the cell parameters and atomic positions to
1414
obtain the equilibrium crystal structure. This includes an average over time, and an average over the replicated unit

output/.gitkeep

Whitespace-only changes.

run.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
import argparse
2-
import subprocess
3-
from kim_tools.test_driver.core import query_crystal_structures
41
from test_driver.test_driver import TestDriver
2+
from ase.build import bulk
53

4+
atoms = bulk("Al")
5+
td=TestDriver("EAM_Dynamo_ErcolessiAdams_1994_Al__MO_123629422045_006")
6+
print(td(
7+
atoms,
8+
temperature_K=300,
9+
target_size=500,
10+
lammps_command= "lmp",
11+
))
612

13+
14+
15+
16+
"""
717
if __name__ == '__main__':
818
# Argument parsing
919
parser = argparse.ArgumentParser(description='Pass arguments to the KIM test driver')
@@ -35,3 +45,4 @@
3545
except Exception as e:
3646
print(f"Got exception {repr(e)}")
3747
test_driver.write_property_instances_to_file()
48+
"""

runner

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@ if temperature_K != "":
1919
else:
2020
print("No temperature given")
2121
temperature_K = 0
22+
pressure_eV_angstrom3 = input("pressure (eV/angstrom^3)?\n")
23+
if pressure_eV_angstrom3 != "":
24+
pressure_eV_angstrom3 = float(pressure_eV_angstrom3)
25+
print(pressure_eV_angstrom3)
26+
else:
27+
print("No pressure given")
28+
pressure_eV_angstrom3 = None
2229
cell_cauchy_stress_eV_angstrom3 = input(
23-
"Cauchy stress (literal list of floats, Voigt order xx,yy,zz,yz,xz,xy, eV/A^3)?\n"
30+
"Cauchy stress (literal list of floats, Voigt order xx,yy,zz,yz,xz,xy, eV/angstrom^3)?\n"
2431
).split()
2532
if cell_cauchy_stress_eV_angstrom3 != []:
2633
cell_cauchy_stress_eV_angstrom3 = [
@@ -29,7 +36,7 @@ if cell_cauchy_stress_eV_angstrom3 != []:
2936
print(cell_cauchy_stress_eV_angstrom3)
3037
else:
3138
print("No stress given")
32-
cell_cauchy_stress_eV_angstrom3 = [0, 0, 0, 0, 0, 0]
39+
cell_cauchy_stress_eV_angstrom3 = None
3340
runtime_args_text = input("Runtime arguments (literal dictonary)?\n")
3441
if runtime_args_text != "":
3542
runtime_args = literal_eval(runtime_args_text)
@@ -42,24 +49,40 @@ query_result = literal_eval(
4249
)
4350
print(json.dumps(query_result, indent=2))
4451
test = TestDriver(model_name)
52+
successful_run = False
4553
for structure in query_result:
46-
species_match = (
47-
structure["stoichiometric-species"]["source-value"]
48-
== query_result[0]["stoichiometric-species"]["source-value"]
49-
)
50-
prototype_match = (
51-
structure["prototype-label"]["source-value"]
52-
== query_result[0]["prototype-label"]["source-value"]
53-
)
54-
assert species_match and prototype_match, (
55-
"The input structures must have identical "
56-
"prototype-label and stoichiometric-species"
57-
)
58-
test(
59-
structure,
60-
temperature_K=temperature_K,
61-
cell_cauchy_stress_eV_angstrom3=cell_cauchy_stress_eV_angstrom3,
62-
**runtime_args
63-
)
54+
try:
55+
species_match = (
56+
structure["stoichiometric-species"]["source-value"]
57+
== query_result[0]["stoichiometric-species"]["source-value"]
58+
)
59+
prototype_match = (
60+
structure["prototype-label"]["source-value"]
61+
== query_result[0]["prototype-label"]["source-value"]
62+
)
63+
assert species_match and prototype_match, (
64+
"The input structures must have identical "
65+
"prototype-label and stoichiometric-species"
66+
)
67+
test(
68+
structure,
69+
temperature_K=temperature_K,
70+
pressure_eV_angstrom3=pressure_eV_angstrom3,
71+
cell_cauchy_stress_eV_angstrom3=cell_cauchy_stress_eV_angstrom3,
72+
**runtime_args
73+
)
74+
successful_run = True
75+
except Exception as e:
76+
print(
77+
"The following exception prevented one of the parameter sets from "
78+
f"returning a result: \n{repr(e)}"
79+
)
80+
exc = e
81+
if not successful_run:
82+
raise RuntimeError(
83+
"No parameter sets successfully ran. Last exception is attached, "
84+
"see output file for others, if any."
85+
) from exc
86+
test.deduplicate_property_instances(allow_rotation=False)
6487
test.write_property_instances_to_file()
6588
copy("kim-tools.log", "output/kim-tools.log")

test_driver/helper_functions.py

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99

1010

1111
def run_lammps(modelname: str, temperature_K: float, pressure_bar: float, timestep_ps: float,
12-
number_sampling_timesteps: int, species: List[str],
12+
thermo_sampling_period: int, species: List[str],
1313
msd_threshold_angstrom_squared_per_sampling_timesteps: float, number_msd_timesteps: int,
14-
lammps_command: str, random_seed: int) -> Tuple[str, str, str, str]:
14+
rlc_run_length: int, rlc_n_every: int, output_dir: str, equilibration_plots: bool, lammps_command: str,
15+
random_seed: int) -> Tuple[str, str, str, str, str]:
1516
"""
1617
Run LAMMPS NPT simulation with the given parameters.
1718
@@ -33,9 +34,9 @@ def run_lammps(modelname: str, temperature_K: float, pressure_bar: float, timest
3334
:param timestep_ps:
3435
Timestep in picoseconds.
3536
:type timestep_ps: float
36-
:param number_sampling_timesteps:
37+
:param thermo_sampling_period:
3738
Number of timesteps for sampling thermodynamic quantities.
38-
:type number_sampling_timesteps: int
39+
:type thermo_sampling_period: int
3940
:param species:
4041
List of chemical species in the system.
4142
:type species: List[str]
@@ -47,6 +48,19 @@ def run_lammps(modelname: str, temperature_K: float, pressure_bar: float, timest
4748
Before the mean-squared displacement is monitored, the system will be equilibrated for the same number of
4849
timesteps.
4950
:type number_msd_timesteps: int
51+
:param rlc_run_length:
52+
Number of timesteps after which kim-convergence will check for convergence.
53+
This is also the timestep interval for generated trajectories.
54+
:type rlc_run_length: int
55+
:param rlc_n_every:
56+
Number of timesteps between storage of values for the run-length control in kim-convergence.
57+
:type rlc_n_every: int
58+
:param output_dir:
59+
Directory to store the output files.
60+
:type output_dir: str
61+
:param equilibration_plots:
62+
Whether to plot the equilibration plots.
63+
:type equilibration_plots: bool
5064
:param lammps_command:
5165
Command to run LAMMPS (e.g., "mpirun -np 4 lmp_mpi" or "lmp").
5266
:type lammps_command: str
@@ -55,15 +69,19 @@ def run_lammps(modelname: str, temperature_K: float, pressure_bar: float, timest
5569
:type random_seed: int
5670
5771
:return:
58-
A tuple containing paths to the LAMMPS log file, restart file, full average position file, and full average cell
59-
file.
60-
:rtype: Tuple[str, str, str, str]
72+
A tuple containing paths to the LAMMPS log file, restart file, full average position file, full average cell
73+
file, and melted crystal dump file (only exists if crystal melted).
74+
:rtype: Tuple[str, str, str, str, str]
6175
"""
62-
pdamp = timestep_ps * 100.0
63-
tdamp = timestep_ps * 1000.0
64-
65-
log_filename = "output/lammps.log"
66-
restart_filename = "output/final_configuration.restart"
76+
pdamp = timestep_ps * 1000.0
77+
tdamp = timestep_ps * 100.0
78+
79+
# Lammps will be run directly in output_dir so all paths are with respect to that directory.
80+
log_filename = "lammps.log"
81+
restart_filename = "final_configuration.restart"
82+
melted_crystal_filename = "melted_crystal.dump"
83+
average_position_filename = "average_position.dump"
84+
average_cell_filename = "average_cell.dump"
6785
variables = {
6886
"modelname": modelname,
6987
"temperature": temperature_K,
@@ -72,43 +90,48 @@ def run_lammps(modelname: str, temperature_K: float, pressure_bar: float, timest
7290
"pressure": pressure_bar,
7391
"pressure_damping": pdamp,
7492
"timestep": timestep_ps,
75-
"number_sampling_timesteps": number_sampling_timesteps,
93+
"thermo_sampling_period": thermo_sampling_period,
7694
"species": " ".join(species),
77-
"average_position_filename": "output/average_position.dump.*",
78-
"average_cell_filename": "output/average_cell.dump",
95+
"zero_temperature_crystal_filename": "zero_temperature_crystal.lmp",
96+
"average_position_filename": f"{average_position_filename}.*",
97+
"average_cell_filename": average_cell_filename,
7998
"write_restart_filename": restart_filename,
80-
"trajectory_filename": "output/trajectory.lammpstrj",
81-
"msd_trajectory_filename": "output/msd_trajectory.lammpstrj",
99+
"trajectory_filename": "trajectory.lammpstrj",
100+
"msd_trajectory_filename": "msd_trajectory.lammpstrj",
82101
"msd_threshold": msd_threshold_angstrom_squared_per_sampling_timesteps,
83102
"msd_timesteps": number_msd_timesteps,
84-
"melted_crystal_output": "output/melted_crystal.dump"
103+
"rlc_run_length": rlc_run_length,
104+
"rlc_n_every": rlc_n_every,
105+
"melted_crystal_output": melted_crystal_filename
85106
}
86107

87108
command = (
88109
f"{lammps_command} "
89110
+ " ".join(f"-var {key} '{item}'" for key, item in variables.items())
90111
+ f" -log {log_filename}"
91-
+ " -in npt.lammps")
112+
+ f" -in npt.lammps")
92113

93-
subprocess.run(command, check=True, shell=True)
114+
subprocess.run(command, check=True, shell=True, cwd=output_dir)
94115

95-
plot_property_from_lammps_log(log_filename, ("v_vol_metal", "v_temp_metal", "v_enthalpy_metal"))
116+
if equilibration_plots:
117+
plot_property_from_lammps_log(f"{output_dir}/{log_filename}",
118+
("v_vol_metal", "v_temp_metal", "v_enthalpy_metal"))
96119

97-
# 10000 offset from MSD detection during which kim_convergence was not used.
98-
equilibration_time = extract_equilibration_step_from_logfile(log_filename) + 10000
99-
# Round to next multiple of 10000.
100-
equilibration_time = int(ceil(equilibration_time / 10000.0)) * 10000
120+
equilibration_time = extract_equilibration_step_from_logfile(f"{output_dir}/{log_filename}")
121+
# Round to next multiple of rlc_run_length.
122+
equilibration_time = int(ceil(equilibration_time / float(rlc_run_length))) * rlc_run_length
101123

102-
full_average_position_file = "output/average_position.dump.full"
103-
compute_average_positions_from_lammps_dump("output",
104-
"average_position.dump",
124+
full_average_position_file = f"{output_dir}/{average_position_filename}.full"
125+
compute_average_positions_from_lammps_dump(output_dir,
126+
average_position_filename,
105127
full_average_position_file, equilibration_time)
106128

107-
full_average_cell_file = "output/average_cell.dump.full"
108-
compute_average_cell_from_lammps_dump("output/average_cell.dump",
129+
full_average_cell_file = f"{output_dir}/{average_cell_filename}.full"
130+
compute_average_cell_from_lammps_dump(f"{output_dir}/{average_cell_filename}",
109131
full_average_cell_file, equilibration_time)
110132

111-
return log_filename, restart_filename, full_average_position_file, full_average_cell_file
133+
return (f"{output_dir}/{log_filename}", f"{output_dir}/{restart_filename}", full_average_position_file,
134+
full_average_cell_file, f"{output_dir}/{melted_crystal_filename}")
112135

113136

114137
def plot_property_from_lammps_log(in_file_path: str, property_names: Iterable[str]) -> None:

test_driver/npt.lammps

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ boundary p p p
88
neigh_modify delay 0 every 1 check yes one 4000
99

1010
# Read initial zero-temperature crystal.
11-
read_data output/zero_temperature_crystal.lmp
11+
read_data ${zero_temperature_crystal_filename}
1212

1313
# Change to triclinic box.
1414
change_box all triclinic
@@ -66,23 +66,23 @@ run 0
6666
velocity all scale $(v_temperature*v__u_temperature)
6767

6868
# Write trajectory.
69-
dump msd_traj all atom 10000 ${msd_trajectory_filename}
69+
dump msd_traj all atom ${rlc_run_length} ${msd_trajectory_filename}
7070

7171
# Compute mean squared displacement
7272
compute msd all msd com yes
7373

7474
# Perform a short run to equilibrate MSD
7575
run ${msd_timesteps}
76-
76+
7777
# Calculate slope of MSD to detect diffusion
78-
fix msd_vector all vector ${number_sampling_timesteps} c_msd[4]
78+
fix msd_vector all vector ${thermo_sampling_period} c_msd[4]
7979
variable msd_slope equal slope(f_msd_vector)
8080

8181
# Thermodynamic output
8282
thermo_style custom step v_pe_metal v_ke_metal v_temp_metal v_press_metal v_vol_metal &
8383
v_xlo_metal v_xhi_metal v_ylo_metal v_yhi_metal v_zlo_metal v_zhi_metal &
8484
v_xy_metal v_xz_metal v_yz_metal v_enthalpy_metal c_msd[4] v_msd_slope
85-
thermo ${number_sampling_timesteps}
85+
thermo ${thermo_sampling_period}
8686

8787
# Perform a short run and decide whether or not to quit
8888
run ${msd_timesteps}
@@ -96,13 +96,13 @@ undump msd_traj
9696
reset_timestep 0
9797

9898
# Write trajectory.
99-
dump traj all atom 10000 ${trajectory_filename}
99+
dump traj all atom ${rlc_run_length} ${trajectory_filename}
100100

101101
# Set up logging of thermodynamic information.
102102
thermo_style custom step v_pe_metal v_ke_metal v_temp_metal v_press_metal v_vol_metal &
103103
v_xlo_metal v_xhi_metal v_ylo_metal v_yhi_metal v_zlo_metal v_zhi_metal &
104104
v_xy_metal v_xz_metal v_yz_metal v_enthalpy_metal
105-
thermo ${number_sampling_timesteps}
105+
thermo ${thermo_sampling_period}
106106

107107
# Store unwrapped coordinates for every atom.
108108
# Note that Lammps cannot store scaled unwrapped coordinates (it can only dump them).
@@ -125,34 +125,33 @@ variable ysu atom "(c_up[2]-ylo)/(yhi-ylo) - (yz*(c_up[3]-zlo))/((yhi-ylo)*(zhi-
125125
variable zsu atom "(c_up[3]-zlo)/(zhi-zlo)"
126126

127127
# Since Lammps can dump scaled unwrapped coordinates, one can test above variables by including the following two lines:
128-
# dump test all custom 10000 output/test.dump.* id xsu ysu zsu v_xsu v_ysu v_zsu
129-
# dump_modify test delay 10000
128+
# dump test all custom ${rlc_run_length} output/test.dump.* id xsu ysu zsu v_xsu v_ysu v_zsu
129+
# dump_modify test delay ${rlc_run_length}
130130

131131
# Average the scaled unwrapped positions.
132-
# Note that kim-convergence interrupts simulations after 10000 timesteps.
133-
# We write out the average scaled unwrapped positions after every 10000 timesteps.
134-
fix avePos all ave/atom 1 10000 10000 v_xsu v_ysu v_zsu
135-
dump avePosDump all custom 10000 ${average_position_filename} id f_avePos[1] f_avePos[2] f_avePos[3]
132+
# Note that kim-convergence interrupts simulations after ${rlc_run_length} timesteps.
133+
# We write out the average scaled unwrapped positions after every ${rlc_run_length} timesteps.
134+
fix avePos all ave/atom 1 ${rlc_run_length} ${rlc_run_length} v_xsu v_ysu v_zsu
135+
dump avePosDump all custom ${rlc_run_length} ${average_position_filename} id f_avePos[1] f_avePos[2] f_avePos[3]
136136

137137
# Prevent dump at timestep 0.
138-
dump_modify avePosDump delay 10000
138+
dump_modify avePosDump delay ${rlc_run_length}
139139

140140
# Average the cell information.
141141
variable lx_metal equal "v_xhi_metal - v_xlo_metal"
142142
variable ly_metal equal "v_yhi_metal - v_ylo_metal"
143143
variable lz_metal equal "v_zhi_metal - v_zlo_metal"
144-
fix aveCell all ave/time 1 10000 10000 v_lx_metal v_ly_metal v_lz_metal v_xy_metal v_xz_metal v_yz_metal file ${average_cell_filename}
144+
fix aveCell all ave/time 1 ${rlc_run_length} ${rlc_run_length} v_lx_metal v_ly_metal v_lz_metal v_xy_metal v_xz_metal v_yz_metal file ${average_cell_filename}
145145

146146
# Set up convergence check with kim-convergence.
147-
# python run_length_control input 8 SELF 1 variable vol_metal variable temp_metal variable enthalpy_metal format pissssss file run_length_control.py
148-
python run_length_control input 20 SELF 1 variable vol_metal variable temp_metal variable enthalpy_metal variable lx_metal variable ly_metal variable lz_metal variable xy_metal variable xz_metal variable yz_metal format pissssssssssssssssss file run_length_control.py
147+
python run_length_control input 20 SELF ${rlc_n_every} variable vol_metal variable temp_metal variable enthalpy_metal variable lx_metal variable ly_metal variable lz_metal variable xy_metal variable xz_metal variable yz_metal format pissssssssssssssssss file run_length_control.py
149148

150149
# Run until converged.
151150
python run_length_control invoke
152151

153-
# Run 10000 steps more in the converged regime.
152+
# Run ${rlc_run_length} steps more in the converged regime.
154153
# This makes sure that we have average fractional coordinates and cell information that were obtained entirely in equilibrium.
155-
run 10000
154+
run ${rlc_run_length}
156155

157156
# Reset.
158157
unfix NPT

0 commit comments

Comments
 (0)