Source code for mchammer.data_containers.data_container
""" Data container class. """
import numpy as np
from .base_data_container import BaseDataContainer
from ..data_analysis import analyze_data
[docs]
class DataContainer(BaseDataContainer):
"""
Data container for storing information concerned with
Monte Carlo simulations performed with :program:`mchammer`.
This is the data container the thermodynamic ensembles write.
On top of the storage and access the base class provides, it analyzes
scalar observables through :func:`get_average` and :func:`analyze_data`,
which estimates the correlation length and the statistical error.
The section :ref:`data_container` describes the container in more detail.
Parameters
----------
structure
Reference atomic structure associated with the data container.
ensemble_parameters
Parameters associated with the underlying ensemble.
metadata
Metadata associated with the data container.
Example
-------
The following snippet runs a short simulation in the canonical ensemble and
analyzes the potential along the trajectory.
The parameters of the cluster expansion are made up to keep the example
self-contained::
>>> from ase.build import bulk
>>> from icet import ClusterExpansion, ClusterSpace
>>> from mchammer.calculators import ClusterExpansionCalculator
>>> from mchammer.ensembles import CanonicalEnsemble
>>> # prepare cluster expansion
>>> prim = bulk('Au')
>>> cs = ClusterSpace(prim, cutoffs=[4.3], chemical_symbols=['Ag', 'Au'])
>>> ce = ClusterExpansion(cs, [0, 0, 0.1, -0.02])
>>> # prepare initial configuration
>>> structure = prim.repeat(3)
>>> for k in range(5):
... structure[k].symbol = 'Ag'
>>> # set up and run the simulation
>>> calculator = ClusterExpansionCalculator(structure, ce)
>>> mc = CanonicalEnsemble(structure=structure, calculator=calculator,
... temperature=600)
>>> mc.run(len(structure) * 50)
>>> # analyze the potential, skipping the first steps as equilibration
>>> dc = mc.data_container
>>> print(dc.data)
>>> mean = dc.get_average('potential', start=len(structure) * 10)
>>> summary = dc.analyze_data('potential', start=len(structure) * 10)
>>> print(summary['error_estimate'])
"""
[docs]
def analyze_data(self, tag: str, start: int | None = None, max_lag: int | None = None) -> dict:
"""
Returns a statistical analysis of a scalar observable.
Parameters
----------
tag
Name of the observable to analyze.
start
Smallest trial step to include.
By default all records are included.
max_lag
Maximum lag between two points in the data series when computing
the autocorrelation function.
By default the length of the data series minus one.
Returns
-------
dict
The mean, the standard deviation, the correlation length in trial
steps, and the error estimate of the mean at 95 % confidence, under
the keys ``mean``, ``std``, ``correlation_length``, and
``error_estimate``.
The correlation length and the error estimate are ``nan`` when the
autocorrelation function does not decay within the data series.
Raises
------
ValueError
If the observable is not in the data container.
ValueError
If the observable is not scalar.
ValueError
If the observations are not evenly spaced in trial steps.
"""
# get data for tag
if tag in ['trajectory', 'occupations']:
raise ValueError('{} is not scalar'.format(tag))
steps, data = self.get('mctrial', tag, start=start)
# check that steps are evenly spaced
diff = np.diff(steps)
step_length = diff[0]
if not np.allclose(step_length, diff):
raise ValueError('data records must be evenly spaced.')
summary = analyze_data(data, max_lag=max_lag)
summary['correlation_length'] *= step_length # in mc-trials
return summary
[docs]
def get_average(self, tag: str, start: int | None = None) -> float:
"""
Returns the average of a scalar observable.
Parameters
----------
tag
Name of the observable to average.
start
Smallest trial step to include.
By default all records are included.
Raises
------
ValueError
If the observable is not in the data container.
ValueError
If the observable is not scalar.
"""
if tag in ['trajectory', 'occupations']:
raise ValueError('{} is not scalar'.format(tag))
data = self.get(tag, start=start)
return np.mean(data)