Source code for magpy.results

from scipy.integrate import trapz
import numpy as np
import matplotlib.pyplot as plt


[docs]class Results: """Results of a simulation of a single particle cluster The results contain the time-varying magnetisation and field resulting from stochastic simulation of a particle cluster consisting of `N` particles. Args: time (np.ndarray): 1d array of length `M`. Time in seconds for each sample in the results field (np.ndarray): 1d array of length `M`. Field amplitude at each point in time. Field is always applied along the z-axis. x (dict): `{0: np.ndarray, ..., N-1: np.ndarray}` key, value pair is an interger particle id and a corresponding 1d array of length `M` for each of the `N` particles in the cluster. 1d array is the x-coordinate of the particle magnetisation vector at each point in time. y (dict): `{0: np.ndarray, ..., N-1: np.ndarray}` key, value pair is an interger particle id and a corresponding 1d array of length `M` for each of the `N` particles in the cluster. 1d array is the y-coordinate of the particle magnetisation vector at each point in time. z (dict): `{0: np.ndarray, ..., N-1: np.ndarray}` key, value pair is an interger particle id and a corresponding 1d array of length `M` for each of the `N` particles in the cluster. 1d array is the z-coordinate of the particle magnetisation vector at each point in time. N (int): number of particles in the ensemble """ def __init__(self, time, field, x, y, z, N): self.time = time self.field = field self.x = x self.y = y self.z = z self.N = N
[docs] def plot(self): """Plots the magnetisation from the results Plots the x,y,z coordinates of the magnetisation vector for every particle in the particle cluster. Returns: matplotlib figure handle containing the resulting plot axes. """ fg, axs = plt.subplots(nrows=self.N) if self.N==1: axs = [axs] for idx in range(self.N): axs[idx].plot(self.time, self.x[idx], label='x') axs[idx].plot(self.time, self.y[idx], label='y') axs[idx].plot(self.time, self.z[idx], label='z') axs[idx].legend() axs[idx].set_title('Particle {}'.format(idx)) axs[idx].set_xlabel('Reduced time [dimless]') fg.tight_layout() return fg
[docs] def magnetisation(self, direction='z'): """Computes the total magnetisation of the cluster Computes the total time-varying magnetisation of the cluster in a desired direction. The total magnetisation is simply the sum of the individual magnetisation vector components in the specified direction (x,y, or z). Args: direction (str, optional): the direction of magnetisation `x`, `y` or `z`. Default value is `z`. Returns: np.ndarray: 1d array of length `M` the total magnetisation at each point in `self.time`. """ return np.sum([vals for vals in getattr(self, direction).values()], axis=0)
[docs] def final_state(self): """The state of the cluster at the end of the simulation. Returns the state of the particle cluster at the end of the simulation time. Returns: dict: a nested dictionary `{'x': {0: m_x, ..., N-1: m_x}, 'y': ...}` containing the final value of the magnetisation vector for each of the `N` particles in the cluster. """ return { 'x': {k:v[-1] for k,v in self.x.items()}, 'y': {k:v[-1] for k,v in self.y.items()}, 'z': {k:v[-1] for k,v in self.z.items()} }
[docs]class EnsembleResults: """Results from a simulation of an ensemble of particle clusters The EnsembleResults object holds the resulting `magpy.Results` objects for an ensemble of simulated particle clusters. It provides a user-friendly alternative to handling a large collection of `magpy.Results` instances and implemetns methods for computing ensemble-wide properties. Args: results (list[magpy.Results]): results for each particle cluster in the ensemble Attributes: time: (np.ndarray): 1d array of length `M`. Time in seconds for each sample in the ensemble results. field: (np.ndarray): 1d array of length `M`. Field amplitude at each point in time. Field is always applied along the z-axis. """ def __init__(self, results): self.results = results self.time = results[0].time self.field = results[0].field
[docs] def magnetisation(self, direction='z'): """Total magnetisation of each member of the ensemble The total magnetisation of cluster is computed by summing the components of the magnetisation vector for each particle in the cluster. The component (`x`,`y`,`z`) along which the magnetisation may be specified. The default value is `z`, which is the same direction as the applied magnetic field. Args: direction (str, optional): direction of magnetisation `x`, `y` or `z`. Default value is `z`. Returns: list[np.ndarray]: list containing a length `M` 1d array containing the total magnetisation of each particle cluster in the ensemble. """ return [res.magnetisation(direction) for res in self.results]
[docs] def ensemble_magnetisation(self, direction='z'): """Total magnetisation of entire ensemble The total magnetisation of an ensemble of particle clusters. The ensemble magnetisation is the average value of the magnetisation of each particle particle cluster in the ensemble at each point in time. The component (`x`,`y`,`z`) along which the magnetisation may be specified. The default value is `z`, which is the same direction as the applied magnetic field. Args: direction (str, optional): direction of magnetisation `x`, `y` or `z`. Default value is `z`. Returns: np.ndarray: 1d array of length `M` containing the ensemble magnetisation for each point in `self.time` """ return np.sum(self.magnetisation(direction), axis=0) / len(self.results)
[docs] def final_state(self): """State of each ensemble member at the end of the simulation. The final state of each particle cluster in the ensemble at the end of the simulation time. The state of each particle cluster is the value of magnetisation vector of every particle in the cluster. Returns: list[dict]: a list of nested dictionaries like `{'x': {0: m_x, ..., N-1: m_x}, 'y': ...}`. The dictionaries contain the final value of the magnetisation vector for each of the `N` particles in the cluster. """ return [res.final_state() for res in self.results]
[docs] def energy_dissipated(self, start_time=None, end_time=None): """Total energy dissipated by the ensemble. A simulation with a constant or zero applied field will dissipate no energy. The energy dissipated by an ensemble of magnetic particle clusters subjected to an alternating field is the area of the hysteresis loop (magnetisation-field plane). The energy dissipated may be computed for the entire simulation or within a specific time window, defined by `start_time` and `end_time` Args: start_time (double, optional): the start of the time window for computing energy dissipated. Default value `None` uses the start of the simulation. end_time (double, optional): the end of the time window for computing energy dissipated. Default value `None` uses the end of the simulation. Returns: double: total energy dissipated by the ensemble during the time window """ before_mask = (self.time >= start_time) if start_time is not None else True after_mask = (self.time <= end_time) if end_time is not None else True mask = before_mask & after_mask return -get_mu0() * trapz(self.field[mask], self.ensemble_magnetisation()[mask])
[docs] def final_cycle_energy_dissipated(self, field_frequency): """Energy dissipated by the final cycle of the magnetic field. A simulation with a constant or zero applied field will dissipate no energy. The energy dissipated by an ensemble of magnetic particle clusters subjected to an alternating field is the area of the hysteresis loop (magnetisation-field plane). Use this function to compute the energy dissipated by the final cycle (i.e. period) of the applied alternating magnetic field if the total simulation time contains multiple cycles of the field (i.e. is longer than the period of the applied field). A common use case for this is to simulate a large number field cycles to reach equilibrium and then compute the energy dissipated during a single cycle of the field in equilibrium. Args: field_frequency (double): the frequency of the applied magnetic field Returns: double: energy dissipated during the last cycle of the applied magnetic field. """ T = 1./field_frequency return self.energy_dissipated(start_time=self.time[-1] - T)