Skip to content

kups.application.md.analysis

Post-simulation analysis for molecular dynamics.

IsMDInitData

Bases: Protocol

Contract for the init reader group.

Source code in src/kups/application/md/analysis.py
class IsMDInitData(Protocol):
    """Contract for the init reader group."""

    @property
    def atoms(self) -> Table[ParticleId, _IsMDAtoms]: ...

IsMDStepData

Bases: HasPotentialEnergy, HasStressTensor, Protocol

Contract for the step reader group.

Source code in src/kups/application/md/analysis.py
class IsMDStepData(HasPotentialEnergy, HasStressTensor, Protocol):
    """Contract for the step reader group."""

    @property
    def kinetic_energy(self) -> Array: ...

MDAnalysisResult dataclass

Results from MD simulation analysis for a single system.

Attributes:

Name Type Description
potential_energy BlockAverageResult

Average potential energy with SEM (eV).

kinetic_energy BlockAverageResult

Average kinetic energy with SEM (eV).

total_energy BlockAverageResult

Average total energy with SEM (eV).

temperature BlockAverageResult

Average temperature with SEM (K).

energy_drift float

Linear drift rate of total energy (eV/step).

energy_drift_per_atom float

Energy drift normalized by number of atoms.

pressure BlockAverageResult

Average pressure with SEM (Pa).

n_atoms int

Number of atoms in this system.

n_steps int

Number of simulation steps analyzed.

Source code in src/kups/application/md/analysis.py
@plain_dataclass
class MDAnalysisResult:
    """Results from MD simulation analysis for a single system.

    Attributes:
        potential_energy: Average potential energy with SEM (eV).
        kinetic_energy: Average kinetic energy with SEM (eV).
        total_energy: Average total energy with SEM (eV).
        temperature: Average temperature with SEM (K).
        energy_drift: Linear drift rate of total energy (eV/step).
        energy_drift_per_atom: Energy drift normalized by number of atoms.
        pressure: Average pressure with SEM (Pa).
        n_atoms: Number of atoms in this system.
        n_steps: Number of simulation steps analyzed.
    """

    potential_energy: BlockAverageResult
    kinetic_energy: BlockAverageResult
    total_energy: BlockAverageResult
    temperature: BlockAverageResult
    energy_drift: float
    energy_drift_per_atom: float
    pressure: BlockAverageResult
    n_atoms: int
    n_steps: int

analyze_md(init_data, step_data, n_blocks=None)

Analyze MD simulation from pre-loaded data.

Computes thermodynamic averages and energy conservation metrics independently for each system.

Parameters:

Name Type Description Default
init_data IsMDInitData

Initial simulation state with atom positions and system index.

required
step_data IsMDStepData

Per-step thermodynamic data with shape (n_steps, n_systems).

required
n_blocks int | None

Number of blocks for error estimation. If None, uses optimal_block_average to auto-select.

None

Returns:

Type Description
dict[SystemId, MDAnalysisResult]

Per-system analysis results keyed by SystemId.

Source code in src/kups/application/md/analysis.py
def analyze_md(
    init_data: IsMDInitData,
    step_data: IsMDStepData,
    n_blocks: int | None = None,
) -> dict[SystemId, MDAnalysisResult]:
    """Analyze MD simulation from pre-loaded data.

    Computes thermodynamic averages and energy conservation metrics
    independently for each system.

    Args:
        init_data: Initial simulation state with atom positions and system index.
        step_data: Per-step thermodynamic data with shape ``(n_steps, n_systems)``.
        n_blocks: Number of blocks for error estimation. If None, uses
            optimal_block_average to auto-select.

    Returns:
        Per-system analysis results keyed by ``SystemId``.
    """
    system_index = init_data.atoms.data.system
    n_atoms_per_system = system_index.counts.data

    results: dict[SystemId, MDAnalysisResult] = {}
    for i, sys_id in enumerate(system_index.keys):
        results[sys_id] = _analyze_single_system(
            potential_energy=step_data.potential_energy[:, i],
            kinetic_energy=step_data.kinetic_energy[:, i],
            stress_tensor=step_data.stress_tensor[:, i],
            n_atoms=int(n_atoms_per_system[i]),
            n_blocks=n_blocks,
        )

    return results

analyze_md_file(hdf5_path, n_blocks=None)

Analyze MD simulation results from HDF5 file.

Convenience wrapper that reads HDF5 and delegates to analyze_md.

Parameters:

Name Type Description Default
hdf5_path str | Path

Path to HDF5 file from MD simulation.

required
n_blocks int | None

Number of blocks for error estimation. If None, uses optimal_block_average to auto-select.

None

Returns:

Type Description
dict[SystemId, MDAnalysisResult]

Per-system analysis results keyed by SystemId.

Source code in src/kups/application/md/analysis.py
def analyze_md_file(
    hdf5_path: str | Path,
    n_blocks: int | None = None,
) -> dict[SystemId, MDAnalysisResult]:
    """Analyze MD simulation results from HDF5 file.

    Convenience wrapper that reads HDF5 and delegates to ``analyze_md``.

    Args:
        hdf5_path: Path to HDF5 file from MD simulation.
        n_blocks: Number of blocks for error estimation. If None, uses
            optimal_block_average to auto-select.

    Returns:
        Per-system analysis results keyed by ``SystemId``.
    """

    with HDF5StorageReader[MDLoggedData](hdf5_path) as reader:
        init_data = reader.focus_group(lambda state: state.init)[...]
        step_data = reader.focus_group(lambda state: state.step)[...]

    return analyze_md(init_data, step_data, n_blocks=n_blocks)