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.