Skip to content

Commit

Permalink
Replace system with structure
Browse files Browse the repository at this point in the history
  • Loading branch information
ElliottKasoar committed Mar 1, 2024
1 parent dd98d05 commit c04e8da
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 61 deletions.
10 changes: 6 additions & 4 deletions janus_core/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ def parse_dict_class(value: str):

@app.command()
def singlepoint(
system: Annotated[str, typer.Option(help="Path to system to perform calculations")],
structure: Annotated[
str, typer.Option(help="Path to structure to perform calculations")
],
architecture: Annotated[
str, typer.Option("--arch", help="MLIP architecture to use for calculations")
] = "mace_mp",
Expand Down Expand Up @@ -96,8 +98,8 @@ def singlepoint(
Parameters
----------
system : str
System to simulate.
structure : str
Structure to simulate.
architecture : Optional[str]
MLIP architecture to use for single point calculations.
Default is "mace_mp".
Expand All @@ -119,7 +121,7 @@ def singlepoint(
raise ValueError("calc_kwargs must be a dictionary")

s_point = SinglePoint(
system=system,
structure=structure,
architecture=architecture,
device=device,
read_kwargs=read_kwargs,
Expand Down
78 changes: 41 additions & 37 deletions janus_core/single_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class SinglePoint:
Parameters
----------
system : str
System to simulate.
structure : str
Structure to simulate.
architecture : Literal[architectures]
MLIP architecture to use for single point calculations.
Default is "mace_mp".
Expand All @@ -31,36 +31,40 @@ class SinglePoint:
----------
architecture : Literal[architectures]
MLIP architecture to use for single point calculations.
system : str
System to simulate.
structure : str
Path of structure to simulate.
device : Literal[devices]
Device to run MLIP model on.
struct : Union[Atoms, list[Atoms]
ASE Atoms or list of Atoms structures to simulate.
structname : str
Name of structure from its filename.
Methods
-------
read_system(**kwargs)
Read system and system name.
read_structure(**kwargs)
Read structure and structure name.
set_calculator(**kwargs)
Configure calculator and attach to system.
Configure calculator and attach to structure.
run_single_point(properties=None)
Run single point calculations.
"""

def __init__(
self,
system: str,
structure: str,
architecture: Literal[architectures] = "mace_mp",
device: Literal[devices] = "cpu",
read_kwargs: Optional[dict[str, Any]] = None,
calc_kwargs: Optional[dict[str, Any]] = None,
) -> None:
"""
Read the system being simulated and attach an MLIP calculator.
Read the structure being simulated and attach an MLIP calculator.
Parameters
----------
system : str
System to simulate.
structure : str
Path of structure to simulate.
architecture : Literal[architectures]
MLIP architecture to use for single point calculations.
Default is "mace_mp".
Expand All @@ -73,17 +77,17 @@ def __init__(
"""
self.architecture = architecture
self.device = device
self.system = system
self.structure = structure

# Read system and get calculator
# Read structure and get calculator
read_kwargs = read_kwargs if read_kwargs else {}
calc_kwargs = calc_kwargs if calc_kwargs else {}
self.read_system(**read_kwargs)
self.read_structure(**read_kwargs)
self.set_calculator(**calc_kwargs)

def read_system(self, **kwargs) -> None:
def read_structure(self, **kwargs) -> None:
"""
Read system and system name.
Read structure and structure name.
If the file contains multiple structures, only the last configuration
will be read by default.
Expand All @@ -93,14 +97,14 @@ def read_system(self, **kwargs) -> None:
**kwargs
Keyword arguments passed to ase.io.read.
"""
self.sys = read(self.system, **kwargs)
self.sysname = pathlib.Path(self.system).stem
self.struct = read(self.structure, **kwargs)
self.structname = pathlib.Path(self.structure).stem

def set_calculator(
self, read_kwargs: Optional[dict[str, Any]] = None, **kwargs
) -> None:
"""
Configure calculator and attach to system.
Configure calculator and attach to structure.
Parameters
----------
Expand All @@ -114,15 +118,15 @@ def set_calculator(
device=self.device,
**kwargs,
)
if self.sys is None:
if self.struct is None:
read_kwargs = read_kwargs if read_kwargs else {}
self.read_system(**read_kwargs)
self.read_structure(**read_kwargs)

if isinstance(self.sys, list):
for sys in self.sys:
sys.calc = calculator
if isinstance(self.struct, list):
for struct in self.struct:
struct.calc = calculator
else:
self.sys.calc = calculator
self.struct.calc = calculator

def _get_potential_energy(self) -> Union[float, list[float]]:
"""
Expand All @@ -131,12 +135,12 @@ def _get_potential_energy(self) -> Union[float, list[float]]:
Returns
-------
Union[float, list[float]]
Potential energy of system(s).
Potential energy of structure(s).
"""
if isinstance(self.sys, list):
return [sys.get_potential_energy() for sys in self.sys]
if isinstance(self.struct, list):
return [struct.get_potential_energy() for struct in self.struct]

return self.sys.get_potential_energy()
return self.struct.get_potential_energy()

def _get_forces(self) -> Union[ndarray, list[ndarray]]:
"""
Expand All @@ -145,12 +149,12 @@ def _get_forces(self) -> Union[ndarray, list[ndarray]]:
Returns
-------
Union[ndarray, list[ndarray]]
Forces of system(s).
Forces of structure(s).
"""
if isinstance(self.sys, list):
return [sys.get_forces() for sys in self.sys]
if isinstance(self.struct, list):
return [struct.get_forces() for struct in self.struct]

return self.sys.get_forces()
return self.struct.get_forces()

def _get_stress(self) -> Union[ndarray, list[ndarray]]:
"""
Expand All @@ -159,12 +163,12 @@ def _get_stress(self) -> Union[ndarray, list[ndarray]]:
Returns
-------
Union[ndarray, list[ndarray]]
Stress of system(s).
Stress of structure(s).
"""
if isinstance(self.sys, list):
return [sys.get_stress() for sys in self.sys]
if isinstance(self.struct, list):
return [struct.get_stress() for struct in self.struct]

return self.sys.get_stress()
return self.struct.get_stress()

def run_single_point(
self, properties: Optional[Union[str, list[str]]] = None
Expand Down
8 changes: 4 additions & 4 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_singlepoint_help():

def test_singlepoint():
"""Test singlepoint calculation."""
result = runner.invoke(app, ["singlepoint", "--system", DATA_PATH / "NaCl.cif"])
result = runner.invoke(app, ["singlepoint", "--structure", DATA_PATH / "NaCl.cif"])
assert result.exit_code == 0
assert "{'energy': -" in result.stdout
assert "'forces': array" in result.stdout
Expand All @@ -42,7 +42,7 @@ def test_singlepoint_properties():
app,
[
"singlepoint",
"--system",
"--structure",
DATA_PATH / "NaCl.cif",
"--property",
"energy",
Expand All @@ -62,7 +62,7 @@ def test_singlepoint_read_kwargs():
app,
[
"singlepoint",
"--system",
"--structure",
DATA_PATH / "benzene-traj.xyz",
"--read-kwargs",
"{'index': ':'}",
Expand All @@ -80,7 +80,7 @@ def test_singlepoint_calc_kwargs():
app,
[
"singlepoint",
"--system",
"--structure",
DATA_PATH / "NaCl.cif",
"--calc-kwargs",
"{'default_dtype': 'float32'}",
Expand Down
22 changes: 12 additions & 10 deletions tests/test_geom_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@
def test_optimize(architecture, structure, expected, kwargs):
"""Test optimizing geometry using MACE."""
single_point = SinglePoint(
system=DATA_PATH / structure,
structure=DATA_PATH / structure,
architecture=architecture,
calc_kwargs={"model_paths": MODEL_PATH},
)

init_energy = single_point.run_single_point("energy")["energy"]

atoms = optimize(single_point.sys, **kwargs)
atoms = optimize(single_point.struct, **kwargs)

assert atoms.get_potential_energy() < init_energy
assert atoms.get_potential_energy() == pytest.approx(expected)
Expand All @@ -58,15 +58,15 @@ def test_saving_struct(tmp_path):
struct_path = tmp_path / "NaCl.xyz"

single_point = SinglePoint(
system=DATA_PATH / "NaCl.cif",
structure=DATA_PATH / "NaCl.cif",
architecture="mace",
calc_kwargs={"model_paths": MODEL_PATH},
)

init_energy = single_point.run_single_point("energy")["energy"]

optimize(
single_point.sys,
single_point.struct,
struct_kwargs={"filename": struct_path, "format": "extxyz"},
)
opt_struct = read(struct_path)
Expand All @@ -77,19 +77,21 @@ def test_saving_struct(tmp_path):
def test_saving_traj(tmp_path):
"""Test saving optimization trajectory output."""
single_point = SinglePoint(
system=DATA_PATH / "NaCl.cif",
structure=DATA_PATH / "NaCl.cif",
architecture="mace",
calc_kwargs={"model_paths": MODEL_PATH},
)
optimize(single_point.sys, opt_kwargs={"trajectory": str(tmp_path / "NaCl.traj")})
optimize(
single_point.struct, opt_kwargs={"trajectory": str(tmp_path / "NaCl.traj")}
)
traj = read(tmp_path / "NaCl.traj", index=":")
assert len(traj) == 3


def test_traj_reformat(tmp_path):
"""Test saving optimization trajectory in different format."""
single_point = SinglePoint(
system=DATA_PATH / "NaCl.cif",
structure=DATA_PATH / "NaCl.cif",
architecture="mace",
calc_kwargs={"model_paths": MODEL_PATH},
)
Expand All @@ -98,7 +100,7 @@ def test_traj_reformat(tmp_path):
traj_path_xyz = tmp_path / "NaCl-traj.xyz"

optimize(
single_point.sys,
single_point.struct,
opt_kwargs={"trajectory": str(traj_path_binary)},
traj_kwargs={"filename": traj_path_xyz},
)
Expand All @@ -110,10 +112,10 @@ def test_traj_reformat(tmp_path):
def test_missing_traj_kwarg(tmp_path):
"""Test saving optimization trajectory in different format."""
single_point = SinglePoint(
system=DATA_PATH / "NaCl.cif",
structure=DATA_PATH / "NaCl.cif",
architecture="mace",
calc_kwargs={"model_paths": MODEL_PATH},
)
traj_path = tmp_path / "NaCl-traj.xyz"
with pytest.raises(ValueError):
optimize(single_point.sys, traj_kwargs={"filename": traj_path})
optimize(single_point.struct, traj_kwargs={"filename": traj_path})
12 changes: 6 additions & 6 deletions tests/test_single_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@


@pytest.mark.parametrize(
"system, expected, properties, prop_key, calc_kwargs, idx", test_data
"structure, expected, properties, prop_key, calc_kwargs, idx", test_data
)
def test_potential_energy(system, expected, properties, prop_key, calc_kwargs, idx):
def test_potential_energy(structure, expected, properties, prop_key, calc_kwargs, idx):
"""Test single point energy using MACE calculators."""
calc_kwargs["model_paths"] = MODEL_PATH
single_point = SinglePoint(
system=system, architecture="mace", calc_kwargs=calc_kwargs
structure=structure, architecture="mace", calc_kwargs=calc_kwargs
)
results = single_point.run_single_point(properties)[prop_key]

Expand All @@ -50,7 +50,7 @@ def test_potential_energy(system, expected, properties, prop_key, calc_kwargs, i
def test_single_point_none():
"""Test single point stress using MACE calculator."""
single_point = SinglePoint(
system=DATA_PATH / "NaCl.cif",
structure=DATA_PATH / "NaCl.cif",
architecture="mace",
calc_kwargs={"model_paths": MODEL_PATH},
)
Expand All @@ -63,13 +63,13 @@ def test_single_point_none():
def test_single_point_traj():
"""Test single point stress using MACE calculator."""
single_point = SinglePoint(
system=DATA_PATH / "benzene-traj.xyz",
structure=DATA_PATH / "benzene-traj.xyz",
architecture="mace",
read_kwargs={"index": ":"},
calc_kwargs={"model_paths": MODEL_PATH},
)

assert len(single_point.sys) == 2
assert len(single_point.struct) == 2
results = single_point.run_single_point("energy")
assert results["energy"][0] == pytest.approx(-76.0605725422795)
assert results["energy"][1] == pytest.approx(-74.80419118083256)

0 comments on commit c04e8da

Please sign in to comment.