Specifying models for the plasma fields

For each named field specified by a FieldRequest object in the analysis, a corresponding model for the field is required. Field models are classes which inherit from the FieldModel abstract base-class.

Like DiagnosticModel objects, FieldModel object also specify their required parameters through a parameters instance attribute having type Parameters.

The model for a given field cannot depend on the values of another field however, so field models do not require a fields instance attribute.

For example, if we could model the 1D profile of a field as a Gaussian function using the following field model:

from numpy import exp
from midas.models import FieldModel
from midas import Parameters, ParameterVector


class GaussianField(FieldModel):
    def __init__(self, field_name: str, axis: ndarray, axis_name: str):
        self.name = field_name
        self.axis = axis
        self.axis_name = axis_name

        # set up names for the parameters based on the given field name
        self.amplitude = f"{field_name}_amplitude"
        self.centre = f"{field_name}_centre"
        self.width = f"{field_name}_width"
        # create the parameter set
        self.parameters = Parameters(
            ParameterVector(name=self.amplitude, size=1),
            ParameterVector(name=self.centre, size=1),
            ParameterVector(name=self.width, size=1),
        )

    def get_values(
        self, parameters: dict[str, ndarray], field: FieldRequest
    ) -> ndarray:
        # retrieve the coordinate values from the given FieldRequest
        coords = field.coordinates[self.axis_name]
        # build the Gaussian profile
        z = (coords - parameters[self.centre]) / parameters[self.width]
        field_values = parameters[self.amplitude] * exp(-0.5*z**2)
        return field_values

    def get_values_and_jacobian(
        self, parameters: dict[str, ndarray], field: FieldRequest
    ) -> tuple[ndarray, dict[str, ndarray]]:
        # retrieve the coordinate values from the given FieldRequest
        coords = field.coordinates[self.axis_name]
        # build the gaussian profile
        z = (coords - parameters[self.centre]) / parameters[self.width]
        g = exp(-0.5*z**2)
        field_values = parameters[self.amplitude] * g
        # calculate some derivatives required for the jacobians
        dg_dz = -z * g
        dz_dw = -z / parameters[self.width]
        dz_dc = coords / parameters[self.width]
        # build the jacobians dictionary
        jacobians = {
            self.amplitude: g,
            self.centre: dg_dz * dz_dc,
            self.width: dg_dz * dz_dw,
        }
        return field_values, jacobians