Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 152 additions & 1 deletion dpgen/generator/arginfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,16 +621,167 @@ def model_devi_amber_args() -> list[Argument]:
]


def model_devi_calypso_args() -> list[Argument]:
"""CALYPSO engine arguments."""
doc_model_devi_jobs = (
"Settings for CALYPSO structure generation and model deviation. "
"For native CALYPSO mode, each dict in the list specifies the "
"structure-generation settings for one or more iterations. In "
"calypso_input_path mode, this key is still required by the current "
"runtime but may be an empty list."
)
doc_times = "List of iteration indices when this job should be executed."
doc_nameofatoms = "Element symbols of the different chemical species."
doc_numberofatoms = "Number of atoms for each chemical species in one formula unit."
doc_numberofformula = "Range of formula units per cell as [min, max]."
doc_volume = (
"Volume per formula unit in angstrom^3. If omitted, CALYPSO "
"determines the volume automatically."
)
doc_distanceofion = (
"Minimal distances between atoms of each chemical species in "
"angstrom. The matrix shape should match the number of species."
)
doc_psoratio = "Proportion of structures generated by the PSO algorithm (0.0-1.0)."
doc_popsize = "Population size for structure generation."
doc_maxstep = "Maximum number of CALYPSO optimization steps."
doc_icode = "Interface code for local optimization: 1=VASP, 2=SIESTA, 3=GULP."
doc_split = "Whether to split calculations. Use 'T' for true or 'F' for false."
doc_vsc = (
"Variable Stoichiometry Control. Use 'T' to enable it or 'F' to disable it."
)
doc_maxnumatom = "Maximum number of atoms in the unit cell when VSC is 'T'."
doc_ctrlrange = (
"Variation range for each atom type when VSC is 'T'. The matrix shape "
"should match the number of species."
)
doc_pstress = "Target pressure values in GPa."
doc_fmax = "Force convergence criterion for local optimization in eV/angstrom."
doc_calypso_input_path = (
"Directory containing pre-existing CALYPSO input.dat files. When this "
"is set, DP-GEN copies the provided input files instead of generating "
"them from model_devi_jobs."
)
doc_model_devi_max_iter = (
"Maximum model-deviation iteration index when using calypso_input_path."
)
doc_vsc_mode = "Enable variable stoichiometry mode for external input files."

scalar_or_singleton_float = [float, list[float]]
scalar_or_singleton_int = [int, list[int]]

return [
Argument(
"model_devi_jobs",
list,
optional=False,
repeat=True,
doc=doc_model_devi_jobs,
sub_fields=[
Argument("times", list[int], optional=False, doc=doc_times),
Argument("NameOfAtoms", list[str], optional=False, doc=doc_nameofatoms),
Argument(
"NumberOfAtoms",
list[int],
optional=False,
doc=doc_numberofatoms,
),
Argument(
"NumberOfFormula",
list[int],
optional=True,
default=[1, 1],
doc=doc_numberofformula,
),
Argument(
"Volume",
scalar_or_singleton_float,
optional=True,
doc=doc_volume,
),
Argument(
"DistanceOfIon",
list[list[float]],
optional=False,
doc=doc_distanceofion,
),
Argument(
"PsoRatio",
scalar_or_singleton_float,
optional=True,
default=0.6,
doc=doc_psoratio,
),
Argument(
"PopSize",
scalar_or_singleton_int,
optional=True,
default=30,
doc=doc_popsize,
),
Argument(
"MaxStep",
scalar_or_singleton_int,
optional=True,
default=5,
doc=doc_maxstep,
),
Argument(
"ICode",
scalar_or_singleton_int,
optional=True,
default=1,
doc=doc_icode,
),
Argument("Split", str, optional=True, default="T", doc=doc_split),
Argument("VSC", str, optional=True, default="F", doc=doc_vsc),
Argument(
"MaxNumAtom",
scalar_or_singleton_int,
optional=True,
doc=doc_maxnumatom,
),
Argument(
"CtrlRange",
list[list[int]],
optional=True,
doc=doc_ctrlrange,
),
Argument(
"PSTRESS",
scalar_or_singleton_float,
optional=True,
default=[0.001],
doc=doc_pstress,
),
Argument(
"fmax",
scalar_or_singleton_float,
optional=True,
default=0.01,
doc=doc_fmax,
),
],
),
Argument("calypso_input_path", str, optional=True, doc=doc_calypso_input_path),
Argument(
"model_devi_max_iter", int, optional=True, doc=doc_model_devi_max_iter
),
Argument("vsc", bool, optional=True, default=False, doc=doc_vsc_mode),
]


def model_devi_args() -> list[Variant]:
doc_model_devi_engine = "Engine for the model deviation task."
doc_amber = "Amber DPRc engine. The command argument in the machine file should be path to sander."
doc_calypso = "CALYPSO structure-generation engine for crystal prediction."
return [
Variant(
"model_devi_engine",
[
Argument("lammps", dict, model_devi_lmp_args(), doc="LAMMPS"),
Argument("amber", dict, model_devi_amber_args(), doc=doc_amber),
Argument("calypso", dict, [], doc="TODO: add doc"),
Argument("calypso", dict, model_devi_calypso_args(), doc=doc_calypso),
Argument("gromacs", dict, [], doc="TODO: add doc"),
],
default_tag="lammps",
Expand Down
30 changes: 23 additions & 7 deletions dpgen/generator/lib/make_calypso.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
import numpy as np


def _normalize_calypso_scalar(value, name):
if isinstance(value, list):
if len(value) != 1:
raise ValueError(f"{name} should be a scalar or a single-item list")
return value[0]
return value


def _normalize_calypso_pressures(value):
if isinstance(value, list):
return value
return [value]


def make_calypso_input(
nameofatoms,
numberofatoms,
Expand Down Expand Up @@ -177,11 +191,13 @@ def _make_model_devi_native_calypso(iter_index, model_devi_jobs, calypso_run_opt
numberofatoms = cur_job.get("NumberOfAtoms")
numberofformula = cur_job.get("NumberOfFormula", [1, 1])
volume = cur_job.get("Volume")
if volume is not None:
volume = _normalize_calypso_scalar(volume, "Volume")
distanceofion = cur_job.get("DistanceOfIon")
psoratio = cur_job.get("PsoRatio", 0.6)
popsize = cur_job.get("PopSize", 30)
maxstep = cur_job.get("MaxStep", 5)
icode = cur_job.get("ICode", 1)
psoratio = _normalize_calypso_scalar(cur_job.get("PsoRatio", 0.6), "PsoRatio")
popsize = _normalize_calypso_scalar(cur_job.get("PopSize", 30), "PopSize")
maxstep = _normalize_calypso_scalar(cur_job.get("MaxStep", 5), "MaxStep")
icode = _normalize_calypso_scalar(cur_job.get("ICode", 1), "ICode")
split = cur_job.get("Split", "T")
# Cluster

Expand All @@ -192,12 +208,12 @@ def _make_model_devi_native_calypso(iter_index, model_devi_jobs, calypso_run_opt
ctrlrange = None
vsc = cur_job.get("VSC", "F")
if vsc == "T":
maxnumatom = cur_job.get("MaxNumAtom")
maxnumatom = _normalize_calypso_scalar(cur_job.get("MaxNumAtom"), "MaxNumAtom")
ctrlrange = cur_job.get("CtrlRange")
# Optimization
fmax = cur_job.get("fmax", 0.01)
fmax = _normalize_calypso_scalar(cur_job.get("fmax", 0.01), "fmax")
# pstress is a List which contains the target stress
pstress = cur_job.get("PSTRESS", [0.001])
pstress = _normalize_calypso_pressures(cur_job.get("PSTRESS", [0.001]))
# pressures
for press_idx, temp_calypso_run_opt_path in enumerate(calypso_run_opt_path):
# cur_press
Expand Down
5 changes: 4 additions & 1 deletion dpgen/generator/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
from dpgen.generator.lib.make_calypso import (
_make_model_devi_buffet,
_make_model_devi_native_calypso,
_normalize_calypso_pressures,
)
from dpgen.generator.lib.parse_calypso import (
_parse_calypso_dis_mtx,
Expand Down Expand Up @@ -1356,7 +1357,9 @@ def make_model_devi(iter_index, jdata, mdata):
if iter_index in jobbs.get("times"):
cur_job = model_devi_jobs[iiidx]

pressures_list = cur_job.get("PSTRESS", [0.0001])
pressures_list = _normalize_calypso_pressures(
cur_job.get("PSTRESS", [0.0001])
)
for temp_idx in range(len(pressures_list)):
calypso_run_opt_path.append(
"%s.%03d" % (_calypso_run_opt_path, temp_idx) # noqa: UP031
Expand Down
21 changes: 21 additions & 0 deletions tests/generator/test_calypso.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import shutil
import sys
import unittest
from pathlib import Path
Expand All @@ -8,6 +9,8 @@
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
__package__ = "generator"

from dpgen.generator.lib.make_calypso import _make_model_devi_native_calypso

from .context import (
_parse_calypso_dis_mtx,
_parse_calypso_input,
Expand Down Expand Up @@ -132,6 +135,24 @@ def test_make_calypso_input(self):
os.remove("input.dat")
break

def test_make_native_calypso_accepts_single_item_lists(self):
work_path = Path("calypso_native_test")
run_path = work_path / "gen_stru_analy.000"
if work_path.exists():
shutil.rmtree(work_path)
run_path.mkdir(parents=True)
job = model_devi_jobs["model_devi_jobs"].copy()

_make_model_devi_native_calypso(0, [{**job, "times": [0]}], [str(run_path)])

text = (run_path / "input.dat").read_text()
self.assertIn("Volume = 30", text)
self.assertIn("PsoRatio = 0.6", text)
self.assertIn("PopSize = 5", text)
self.assertIn("PSTRESS = 0.000000", text)
self.assertIn("fmax = 0.010000", text)
shutil.rmtree(work_path)

def test_parse_calypso_input(self):
ret = make_calypso_input(
["Mg", "Al", "Cu"],
Expand Down