diff --git a/docs/docs/tutorials/tutorial1_brownian.ipynb b/docs/docs/tutorials/tutorial1_brownian.ipynb index 1eaed39c..5562adde 100644 --- a/docs/docs/tutorials/tutorial1_brownian.ipynb +++ b/docs/docs/tutorials/tutorial1_brownian.ipynb @@ -330,7 +330,7 @@ "metadata": {}, "outputs": [], "source": [ - "diffusion_experiment.plot_data(slicer=True)" + "diffusion_experiment.plot_data(slicer=True, ymax=4)" ] }, { @@ -366,7 +366,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 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." ] }, { @@ -381,6 +381,7 @@ " resolution_model=vanadium_analysis.instrument_model.resolution_model,\n", ")\n", "instrument_model.resolution_model.fix_all_parameters()\n", + "instrument_model.normalize_resolution()\n", "\n", "diffusion_analysis = Analysis(\n", " display_name='Diffusion Analysis',\n", @@ -565,7 +566,7 @@ "outputs": [], "source": [ "diffusion_model_analysis.fit(fit_method='simultaneous')\n", - "diffusion_model_analysis.plot_data_and_model()" + "diffusion_model_analysis.plot_data_and_model(ymax=2)" ] }, { diff --git a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb index 28169b83..e7308dbf 100644 --- a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb +++ b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb @@ -280,6 +280,7 @@ " resolution_model=res_analysis.instrument_model.resolution_model,\n", ")\n", "instrument_model.resolution_model.fix_all_parameters()\n", + "instrument_model.normalize_resolution()\n", "\n", "\n", "analysis = Analysis(\n", @@ -417,6 +418,7 @@ " resolution_model=res_analysis.instrument_model.resolution_model,\n", ")\n", "instrument_model.resolution_model.fix_all_parameters()\n", + "instrument_model.normalize_resolution()\n", "\n", "# Create the analysis object\n", "mag_analysis = Analysis(\n", diff --git a/src/easydynamics/analysis/analysis_base.py b/src/easydynamics/analysis/analysis_base.py index fe79adff..58f5d7a6 100644 --- a/src/easydynamics/analysis/analysis_base.py +++ b/src/easydynamics/analysis/analysis_base.py @@ -290,6 +290,15 @@ def extra_parameters(self, value: Parameter | list[Parameter]) -> None: # Other methods ############# + def normalize_resolution(self) -> None: + """Normalize the resolution in the InstrumentModel to ensure + that it integrates to 1. + + This is important for accurate fitting and interpretation of the + results. + """ + self.instrument_model.normalize_resolution() + ############# # Private methods ############# diff --git a/src/easydynamics/sample_model/instrument_model.py b/src/easydynamics/sample_model/instrument_model.py index c30beeb4..3023e43a 100644 --- a/src/easydynamics/sample_model/instrument_model.py +++ b/src/easydynamics/sample_model/instrument_model.py @@ -351,6 +351,10 @@ def free_resolution_parameters(self) -> None: """Free all parameters in the resolution model.""" self.resolution_model.free_all_parameters() + def normalize_resolution(self) -> None: + """Normalize the resolution model to have area 1.""" + self.resolution_model.normalize_area() + def get_energy_offset_at_Q(self, Q_index: int) -> Parameter: """Get the energy offset Parameter at a specific Q index. diff --git a/src/easydynamics/sample_model/model_base.py b/src/easydynamics/sample_model/model_base.py index ef2c6fb1..6c1392ea 100644 --- a/src/easydynamics/sample_model/model_base.py +++ b/src/easydynamics/sample_model/model_base.py @@ -351,6 +351,11 @@ def get_component_collection(self, Q_index: int) -> ComponentCollection: ) return self._component_collections[Q_index] + def normalize_area(self) -> None: + """Normalize the area of the model across all Q values.""" + for collection in self._component_collections: + collection.normalize_area() + # ------------------------------------------------------------------ # Private methods # ------------------------------------------------------------------ diff --git a/tests/unit/easydynamics/analysis/test_analysis_base.py b/tests/unit/easydynamics/analysis/test_analysis_base.py index 035a65b2..0869ab80 100644 --- a/tests/unit/easydynamics/analysis/test_analysis_base.py +++ b/tests/unit/easydynamics/analysis/test_analysis_base.py @@ -265,6 +265,21 @@ def test_extra_parameters_setter_invalid_type(self, analysis_base, invalid_extra ): analysis_base.extra_parameters = invalid_extra_parameters + ############# + # Other methods + ############# + + def test_normalize_resolution_calls_instrument_model(self, analysis_base): + with patch.object( + analysis_base.instrument_model, 'normalize_resolution' + ) as mock_normalize_resolution: + analysis_base.normalize_resolution() + mock_normalize_resolution.assert_called_once() + + ############# + # Private methods + ############# + def test_on_experiment_changed_updates_Q(self, analysis_base): # WHEN fake_Q = [1, 2, 3] diff --git a/tests/unit/easydynamics/sample_model/test_instrument_model.py b/tests/unit/easydynamics/sample_model/test_instrument_model.py index 634dea8a..cdf5b48b 100644 --- a/tests/unit/easydynamics/sample_model/test_instrument_model.py +++ b/tests/unit/easydynamics/sample_model/test_instrument_model.py @@ -351,6 +351,16 @@ def test_get_all_variables_with_nonint_Q_index_raises(self, instrument_model): ): instrument_model.get_all_variables(Q_index='invalid_index') + def test_normalize_resolution(self, instrument_model): + # WHEN + instrument_model.resolution_model.normalize_area = MagicMock() + + # THEN + instrument_model.normalize_resolution() + + # EXPECT + instrument_model.resolution_model.normalize_area.assert_called_once() + def test_generate_energy_offsets_Q_none(self, instrument_model): # WHEN instrument_model._Q = None diff --git a/tests/unit/easydynamics/sample_model/test_model_base.py b/tests/unit/easydynamics/sample_model/test_model_base.py index d1ade1e3..5bb47613 100644 --- a/tests/unit/easydynamics/sample_model/test_model_base.py +++ b/tests/unit/easydynamics/sample_model/test_model_base.py @@ -360,6 +360,17 @@ def test_clear_Q_raises_without_confirm(self, model_base): with pytest.raises(ValueError, match='Clearing Q values requires confirmation'): model_base.clear_Q() + def test_normalize_area(self, model_base): + # WHEN + + # THEN + model_base.normalize_area() + + # EXPECT + for collection in model_base._component_collections: + total_area = sum(component.area.value for component in collection.components) + assert total_area == pytest.approx(1.0) + def test_repr(self, model_base): # WHEN repr_str = repr(model_base)