Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/tutorials/sample_model.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.4"
"version": "3.14.5"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/tutorials/tutorial0_more_advanced.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.4"
"version": "3.14.5"
}
},
"nbformat": 4,
Expand Down
47 changes: 13 additions & 34 deletions docs/docs/tutorials/tutorial1_brownian.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -94,32 +94,15 @@
"id": "6c87b01c",
"metadata": {},
"source": [
"We now want to fit the vanadium data to determine our resolution. The scattering from vanadium is almost exclusively incoherent elastic, so we model it as a delta function. We do this by creating a `SampleModel` and adding a `DeltaFunction` component to it. The component acts as a template and gets copied to every `Q` when we attach the `SampleModel` to our `Analysis` object. Let's create the `SampleModel`.\n",
"\n",
"We do not give the `DeltaFunction` a `center` value. In this case, the center will be fixed at 0 energy transfer. We set the start value of the area to 1."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6762faba",
"metadata": {},
"outputs": [],
"source": [
"delta_function = sm.DeltaFunction(name='DeltaFunction', area=1)\n",
"sample_model = sm.SampleModel(components=delta_function)"
"We now want to fit the vanadium data to determine our resolution. The scattering from vanadium is almost exclusively incoherent elastic, so it can be modelled as a delta function. There are two ways to do this. The first is to add a `DeltaFunction` component to a `SampleModel` and next create a `ResolutionModel`. The second method, which we here shall use, is to fit the data to a `SampleModel` and then convert it to a `ResolutionModel`."
]
},
{
"cell_type": "markdown",
"id": "dc82774e",
"metadata": {},
"source": [
"We now want to define our resolution function. We will here model it as a Gaussian. We create a `ComponentCollection` and append the `Gaussian` to it. We can add as many components to our resolution as we like; sometimes you need several Gaussians and other functions to accurately describe the resolution.\n",
"\n",
"We fix the area of the resolution to have value 1. If we did not do this, we would fit both the area of the delta function and of the resolution Gaussian, and the fit would never converge.\n",
"\n",
"We finally insert the components in a `ResolutionModel`"
"We now want to define our resolution function. We will here model it as a Gaussian. We create a `ComponentCollection` and append the `Gaussian` to it. We can add as many components to our resolution as we like; sometimes you need several Gaussians and other functions to accurately describe the resolution."
]
},
{
Expand All @@ -129,19 +112,18 @@
"metadata": {},
"outputs": [],
"source": [
"resolution_components = sm.ComponentCollection()\n",
"vanadium_components = sm.ComponentCollection()\n",
"res_gauss = sm.Gaussian(width=0.1, area=1, name='Res. Gauss')\n",
"res_gauss.area.fixed = True\n",
"resolution_components.append_component(res_gauss)\n",
"resolution_model = sm.ResolutionModel(components=resolution_components)"
"vanadium_components.append_component(res_gauss)\n",
"vanadium_model = sm.SampleModel(components=vanadium_components)"
]
},
{
"cell_type": "markdown",
"id": "088ac17d",
"metadata": {},
"source": [
"The background intensity was not 0, so we also create a background model. We use a `Polynomial` with a single coefficient, i.e. a flat background. We here show how to create the `BackgroundModel` and add the background in a single line. We could of course also add it like we did for the `SampleModel` or first create a `ComponentCollection` like we did for the `ResolutionModel`"
"The background intensity was not 0, so we also create a background model. We use a `Polynomial` with a single coefficient, i.e. a flat background. We here show how to create the `BackgroundModel` and add the background in a single line. We could of course also add it like we did for the `SampleModel` or first create a `ComponentCollection` like we just did for the resolution. "
]
},
{
Expand All @@ -159,7 +141,7 @@
"id": "eae3d14b",
"metadata": {},
"source": [
"We combine the resolution abd background model into an `InstrumentModel`. This model also contains a fittable energy offset to account for instrument misalignment. All components are centered at this energy offset."
"We add the background model to an `InstrumentModel`. This model also contains a fittable energy offset to account for instrument misalignment. All components are centered at this energy offset."
]
},
{
Expand All @@ -170,7 +152,6 @@
"outputs": [],
"source": [
"instrument_model = sm.InstrumentModel(\n",
" resolution_model=resolution_model,\n",
" background_model=background_model,\n",
")"
]
Expand All @@ -193,7 +174,7 @@
"vanadium_analysis = edyn.Analysis(\n",
" display_name='Vanadium Full Analysis',\n",
" experiment=vanadium_experiment,\n",
" sample_model=sample_model,\n",
" sample_model=vanadium_model,\n",
" instrument_model=instrument_model,\n",
")"
]
Expand Down Expand Up @@ -265,7 +246,7 @@
"outputs": [],
"source": [
"# Plot some of fitted parameters as a function of Q\n",
"vanadium_analysis.plot_parameters(names=['DeltaFunction area'])"
"vanadium_analysis.plot_parameters(names=['Res. Gauss area'])"
]
},
{
Expand Down Expand Up @@ -356,7 +337,7 @@
"id": "927b8fb5",
"metadata": {},
"source": [
"We also create a new instrument_model and attach it to our analysis, giving it the resolution model determined in the vanadium analysis. We further fix all parameters in the resolution model and normalize it."
"We also create a new instrument_model and attach it to our analysis. We now give it the sample model from the vanadium fit. All parameters are automatically fixed and the resolution model is normalized to have area 1. "
]
},
{
Expand All @@ -368,10 +349,8 @@
"source": [
"instrument_model = sm.InstrumentModel(\n",
" background_model=background_model,\n",
" resolution_model=vanadium_analysis.instrument_model.resolution_model,\n",
" resolution_model=vanadium_analysis.sample_model,\n",
")\n",
"instrument_model.resolution_model.fix_all_parameters()\n",
"instrument_model.normalize_resolution()\n",
"\n",
"diffusion_analysis = edyn.Analysis(\n",
" display_name='Diffusion Analysis',\n",
Expand Down Expand Up @@ -607,7 +586,7 @@
"source": [
"instrument_model = sm.InstrumentModel(\n",
" background_model=background_model,\n",
" resolution_model=vanadium_analysis.instrument_model.resolution_model,\n",
" resolution_model=vanadium_analysis.sample_model,\n",
")"
]
},
Expand Down Expand Up @@ -726,7 +705,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.4"
"version": "3.14.5"
}
},
"nbformat": 4,
Expand Down
23 changes: 7 additions & 16 deletions docs/docs/tutorials/tutorial2_nanoparticles.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
"source": [
"The resolution function can to a good approximation be modelled as a Gaussian. In truth, it has small Lorentzian tails, but we leave it as an exercise for the interested reader to add this.\n",
"\n",
"We define the `SampleModel` to be a `DeltaFunction`, since there is essentially only elastic scattering present. We also add a constant background using a `Polynomial`. \n",
"We fit the resolution directly as in the previous tutorial, since there is essentially only elastic scattering present. We also add a constant background using a `Polynomial`. \n",
"\n",
"As in Tutorial 1 we place everything in an `Analysis` object and plot the start guesses."
]
Expand All @@ -135,16 +135,12 @@
"metadata": {},
"outputs": [],
"source": [
"delta_function = sm.DeltaFunction(area=100)\n",
"res_sample_model = sm.SampleModel(components=delta_function)\n",
"\n",
"res_resolution_model = sm.ResolutionModel()\n",
"res_sample_model = sm.SampleModel()\n",
"res_components = sm.ComponentCollection()\n",
"res_gauss = sm.Gaussian(area=1, width=0.02)\n",
"res_gauss.area.fixed = True\n",
"res_gauss = sm.Gaussian(area=40, width=0.02)\n",
"\n",
"res_components.append_component(res_gauss)\n",
"res_resolution_model.components = res_components\n",
"res_sample_model.components = res_components\n",
"\n",
"background_model = sm.BackgroundModel()\n",
"polynomial = sm.Polynomial(coefficients=[1.5])\n",
Expand All @@ -153,7 +149,6 @@
"\n",
"\n",
"res_instrument_model = sm.InstrumentModel(\n",
" resolution_model=res_resolution_model,\n",
" background_model=background_model,\n",
")\n",
"\n",
Expand Down Expand Up @@ -267,10 +262,8 @@
"\n",
"instrument_model = sm.InstrumentModel(\n",
" background_model=background_model,\n",
" resolution_model=res_analysis.instrument_model.resolution_model,\n",
" resolution_model=res_analysis.sample_model,\n",
")\n",
"instrument_model.resolution_model.fix_all_parameters()\n",
"instrument_model.normalize_resolution()\n",
"\n",
"\n",
"analysis = edyn.Analysis(\n",
Expand Down Expand Up @@ -407,10 +400,8 @@
"\n",
"instrument_model = sm.InstrumentModel(\n",
" background_model=background_model,\n",
" resolution_model=res_analysis.instrument_model.resolution_model,\n",
" resolution_model=res_analysis.sample_model,\n",
")\n",
"instrument_model.resolution_model.fix_all_parameters()\n",
"instrument_model.normalize_resolution()\n",
"\n",
"# Create the analysis object\n",
"mag_analysis = edyn.Analysis(\n",
Expand Down Expand Up @@ -616,7 +607,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.4"
"version": "3.14.5"
}
},
"nbformat": 4,
Expand Down
29 changes: 18 additions & 11 deletions src/easydynamics/sample_model/instrument_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from easydynamics.sample_model.background_model import BackgroundModel
from easydynamics.sample_model.resolution_model import ResolutionModel
from easydynamics.sample_model.sample_model import SampleModel
from easydynamics.utils.utils import Numeric
from easydynamics.utils.utils import Q_type
from easydynamics.utils.utils import _validate_and_convert_Q
Expand All @@ -29,7 +30,7 @@ def __init__(
display_name: str = 'MyInstrumentModel',
unique_name: str | None = None,
Q: Q_type | None = None,
resolution_model: ResolutionModel | None = None,
resolution_model: ResolutionModel | SampleModel | None = None,
background_model: BackgroundModel | None = None,
energy_offset: Numeric | None = None,
unit: str | sc.Unit = 'meV',
Expand All @@ -45,9 +46,10 @@ def __init__(
The unique name of the InstrumentModel.
Q : Q_type | None, default=None
The Q values where the instrument is modelled.
resolution_model : ResolutionModel | None, default=None
The resolution model of the instrument. If None, an empty resolution model is created
and no resolution convolution is carried out.
resolution_model : ResolutionModel | SampleModel | None, default=None
The resolution model of the instrument. If a SampleModel it will be converted to a
ResolutionModel. If None, an empty resolution model is created and no resolution
convolution is carried out.
background_model : BackgroundModel | None, default=None
The background model of the instrument. If None, an empty background model is created,
and the background evaluates to 0.
Expand All @@ -73,11 +75,13 @@ def __init__(
if resolution_model is None:
self._resolution_model = ResolutionModel()
else:
if not isinstance(resolution_model, ResolutionModel):
if not isinstance(resolution_model, (ResolutionModel, SampleModel)):
raise TypeError(
f'resolution_model must be a ResolutionModel or None, '
f'resolution_model must be a ResolutionModel, a SampleModel or None, '
f'got {type(resolution_model).__name__}'
)
if isinstance(resolution_model, SampleModel):
resolution_model = ResolutionModel.from_sample_model(resolution_model)
self._resolution_model = resolution_model

if background_model is None:
Expand Down Expand Up @@ -122,24 +126,27 @@ def resolution_model(self) -> ResolutionModel:
return self._resolution_model

@resolution_model.setter
def resolution_model(self, value: ResolutionModel) -> None:
def resolution_model(self, value: ResolutionModel | SampleModel) -> None:
"""
Set the resolution model of the instrument.

Parameters
----------
value : ResolutionModel
value : ResolutionModel | SampleModel
The new resolution model of the instrument.

Raises
------
TypeError
If value is not a ResolutionModel.
If value is not a ResolutionModel or SampleModel.
"""
if not isinstance(value, ResolutionModel):
if not isinstance(value, (ResolutionModel, SampleModel)):
raise TypeError(
f'resolution_model must be a ResolutionModel, got {type(value).__name__}'
f'resolution_model must be a ResolutionModel or SampleModel, '
f'got {type(value).__name__}'
)
if isinstance(value, SampleModel):
value = ResolutionModel.from_sample_model(value)
self._resolution_model = value
self._on_resolution_model_change()

Expand Down
Loading
Loading