Source code for pantea.simulation.simulate
from pathlib import Path
from typing import Optional, Protocol
import ase
import ase.io
from pantea.atoms.structure import Structure
from pantea.logger import logger
from pantea.types import Array
[docs]class PotentialInterface(Protocol):
def __call__(self, structure: Structure) -> Array: ...
[docs] def compute_forces(self, structure: Structure) -> Array: ...
[docs]class MDSystemInterface(Protocol):
potential: PotentialInterface
structure: Structure
[docs]class SimulatorInterface(Protocol):
step: int
[docs] def repr_physical_params(self, system: MDSystemInterface) -> None: ...
[docs] def simulate_one_step(self, system: MDSystemInterface) -> None: ...
[docs]def simulate(
system: MDSystemInterface,
simulator: SimulatorInterface,
num_steps: int = 1,
output_freq: Optional[int] = None,
filename: Optional[Path] = None,
append: bool = False,
) -> None:
"""
Simulate system for a given number of steps.
:param system: a system of particles and the interacting potential.
:type system: MDSystemInterface
:param simulator: either a molecular dynamics (MD) or monte carlo (MC) simulator
:type simulator: SimulatorInterface
:param num_steps: number of steps, defaults to 1
:type num_steps: int, optional
:param output_freq: print outputs and/or dump configuration file after this number of steps, defaults to None
:type output_freq: Optional[int], optional
:param filename: output configuration file (e.g., `*.xyz`) as supported in the ASE, defaults to None
:type filename: Optional[Path], optional
:param append: whether append to the exiting configuration file or not, defaults to False
:type append: bool, optional
"""
cls_name = simulator.__class__.__name__
logger.info(f"Running {cls_name} for {num_steps} steps")
if output_freq is None:
output_freq = 1 if num_steps < 100 else int(0.01 * num_steps)
is_output: bool = output_freq > 0
if filename is not None:
filename = Path(filename)
if is_output:
logger.info(
f"Writing atomic configuration into '{filename.name}' "
f"file every {output_freq} steps"
)
if not append:
with open(str(filename), "w"):
pass
# system.structure.forces = system.potential.compute_forces(system.structure)
init_step: int = simulator.step
try:
for _ in range(num_steps):
if is_output and ((simulator.step - init_step) % output_freq == 0):
print(simulator.repr_physical_params(system))
if filename is not None:
atoms = system.structure.to_ase()
ase.io.write(str(filename), atoms, append=True)
simulator.simulate_one_step(system)
except KeyboardInterrupt:
print("KeyboardInterrupt")
if is_output:
print(simulator.repr_physical_params(system))