Skip to main content

Fitting microstructure models

We use the abstract base classes AtomicModel and CompositeModel to implement microstructure models. Each model implements all necessary methods as specific by ModelBlueprint.

Defining base models

A "base model" such as Ball or Stick is an AtomicModel. The models set their _parameters and _parameter_bounds attributes and override the calculate_signal method to implement their specific behavior:

class FooModel(AtomicModel):
def __init__(self, fixed_parameters=None) -> None:
self._parameters = ["diffusivity"]
self._parameter_bounds =
super().__init__(fixed_parameters)

def calculate_signal(self, acq, mps):
# ensure that fixed_parameters is respected
mps.update(self._parameter_fixed_values)

return np.exp(- acq["b_value"] * mps["diffusivity"])

We can now use our FooModel

>>> model = FooModel()
>>> model.get_trainable_parameters()
[("diffusivity", (1e-9, 3e-6))]

Behavior when fixing parameters is inherited from the implementation in AtomicModel.

>>> model = FooModel(fixed_parameters=)
>>> model.get_trainable_parameters()
[]

Defining composite models

Composite models are defined by inheriting from CompositeModel and specifying the submodels attribute. By default, the signal of a CompositeModel is defined as a linear combination of the signals of its submodels. The weights of the individual submodels are fittable parameters of the CompositeModel.

By overriding the calculate_signal function, a CompositeModel may extend the represented equation by additional terms. The function may make use of _calculate_submodel_signals and _calculate_submodel_fractions. This is a formula $hi$.

::: mismo.models.model_framework.AtomicModel

::: mismo.models.model_framework.CompositeModel

options: members:

  • _calculate_submodel_fractions
  • _calculate_submodel_signals

Fitting models to data

The primary fitting method utilizes LMFit-provided optimization functions.

::: mismo.fitting.fit_lmfit_optimizer