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
22 changes: 21 additions & 1 deletion docs/docs/tutorials/tutorial0_basics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"source": [
"# Imports\n",
"import pooch\n",
"import scipp as sc\n",
"\n",
"import easydynamics as edyn\n",
"import easydynamics.sample_model as sm\n",
Expand Down Expand Up @@ -322,7 +323,7 @@
"metadata": {},
"outputs": [],
"source": [
"print(f'The reduced chi-squared value for Q_index=5 is: {fit_result_all_Q[5].reduced_chi}')\n",
"print(f'The reduced chi-squared value for Q_index=5 is: {fit_result_all_Q[5].reduced_chi2}')\n",
"\n",
"print(f'The minimizer engine is: {fit_result_all_Q[5].minimizer_engine}')"
]
Expand Down Expand Up @@ -363,6 +364,25 @@
"analysis.plot_parameters(names=['Gaussian area'])"
]
},
{
"cell_type": "markdown",
"id": "d6e8ece6",
"metadata": {},
"source": [
"If you wish to customise the plot beyond what is immediately possible with EasyDynamics, you can get the data and model as a scipp datagroup. You may evaluate the model at different energies than the data like this."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3af8c1f1",
"metadata": {},
"outputs": [],
"source": [
"energy = sc.linspace('energy', -3.5, 3.5, num=1001, unit='meV')\n",
"data_and_model = analysis.data_and_model_to_datagroup(energy=energy)"
]
},
{
"cell_type": "markdown",
"id": "842c1f01",
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/tutorials/tutorial2_nanoparticles.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"id": "bca91d3c",
"metadata": {},
"outputs": [],
Expand Down Expand Up @@ -70,7 +70,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": null,
"id": "4fbb90da",
"metadata": {},
"outputs": [],
Expand Down Expand Up @@ -306,7 +306,7 @@
"outputs": [],
"source": [
"analysis.fit()\n",
"analysis.plot_data_and_model(logy=True)"
"analysis.plot_data_and_model(logy=True, autoscale=False)"
]
},
{
Expand Down
1,904 changes: 903 additions & 1,001 deletions pixi.lock

Large diffs are not rendered by default.

110 changes: 95 additions & 15 deletions src/easydynamics/analysis/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,25 +285,35 @@ def plot_data_and_model(

import plopp as pp

data_and_model = self.data_and_model_to_datagroup(
energy=energy,
add_background=add_background,
include_components=plot_components,
)

plot_kwargs_defaults = {
'title': self.display_name,
'linestyle': {'Data': 'none', 'Model': '-'},
'marker': {'Data': 'o', 'Model': None},
'color': {'Data': 'black', 'Model': 'red'},
'markerfacecolor': {'Data': 'none', 'Model': 'none'},
'linestyle': {},
'marker': {},
'color': {},
'markerfacecolor': {},
'keep': 'energy',
}
data_and_model = {
'Data': self.experiment.binned_data,
'Model': self._create_model_array(energy=energy),
}

if plot_components:
components = self._create_components_dataset(
add_background=add_background, energy=energy
)
for key in components:
data_and_model[key] = components[key]
for key in data_and_model:
if key == 'Data':
plot_kwargs_defaults['linestyle'][key] = 'none'
plot_kwargs_defaults['marker'][key] = 'o'
plot_kwargs_defaults['color'][key] = 'black'
plot_kwargs_defaults['markerfacecolor'][key] = 'none'

elif key == 'Model':
plot_kwargs_defaults['linestyle'][key] = '-'
plot_kwargs_defaults['marker'][key] = None
plot_kwargs_defaults['color'][key] = 'red'
plot_kwargs_defaults['markerfacecolor'][key] = 'none'

else:
plot_kwargs_defaults['linestyle'][key] = '--'
plot_kwargs_defaults['marker'][key] = None

Expand All @@ -316,9 +326,79 @@ def plot_data_and_model(
)
for widget in fig.bottom_bar[0].controls.values():
widget.slider_toggler.value = '-o-'

fig.autoscale()
return fig

def data_and_model_to_datagroup(
self,
energy: sc.Variable | None = None,
add_background: bool = True,
include_components: bool = True,
) -> sc.DataGroup:
"""
Create a scipp DataGroup containing the experimental data, model calculation and optionally
the individual components of the model.

Parameters
----------
energy : sc.Variable | None, default=None
The energy values to use for calculating the model. If None, uses the energy from the
experiment.
add_background : bool, default=True
Whether to add background components to the sample model components when creating the
DataGroup.
include_components : bool, default=True
Whether to include the individual components of the model in the DataGroup. If False,
only the total model will be included.

Raises
------
ValueError
If there is no data to include in the DataGroup, or if there are no Q values available
for creating the DataGroup.

TypeError
If add_background is not True or False. If include_components is not True or False.

Returns
-------
sc.DataGroup
A DataGroup containing the experimental data, model calculation, and optionally the
individual components of the model.
"""
if self.experiment.binned_data is None:
raise ValueError('No data to include in DataGroup. Please load data first.')

if self.Q is None:
raise ValueError(
'No Q values available for creating DataGroup. Please check the experiment data.'
)

if not isinstance(add_background, bool):
raise TypeError('add_background must be True or False.')

if not isinstance(include_components, bool):
raise TypeError('include_components must be True or False.')

energy = self._verify_energy(energy)

if energy is None:
energy = self.energy

data_and_model = {
'Data': self.experiment.binned_data,
'Model': self._create_model_array(energy=energy),
}

if include_components:
components = self._create_components_dataset(
add_background=add_background, energy=energy
)
for key in components:
data_and_model[key] = components[key]

return sc.DataGroup(data_and_model)

def parameters_to_dataset(self) -> sc.Dataset:
"""
Creates a scipp dataset with copies of the Parameters in the model.
Expand Down
144 changes: 96 additions & 48 deletions src/easydynamics/analysis/analysis1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,56 +291,128 @@ def plot_data_and_model(
**kwargs : dict[str, Any]
Keyword arguments to pass to the plotting function.

Raises
------
ValueError
If no data is available to plot.

Returns
-------
InteractiveFigure
A plot of the data and model.
"""
import plopp as pp

data_and_model = self.data_and_model_to_datagroup(
energy=energy,
add_background=add_background,
include_components=plot_components,
)

plot_kwargs_defaults = {
'title': self.display_name,
'linestyle': {'Data': 'none', 'Model': '-'},
'marker': {'Data': 'o', 'Model': 'none'},
'color': {'Data': 'black', 'Model': 'red'},
'markerfacecolor': {'Data': 'none', 'Model': 'none'},
'linestyle': {},
'marker': {},
'color': {},
'markerfacecolor': {},
}

for key in data_and_model:
if key == 'Data':
plot_kwargs_defaults['linestyle'][key] = 'none'
plot_kwargs_defaults['marker'][key] = 'o'
plot_kwargs_defaults['color'][key] = 'black'
plot_kwargs_defaults['markerfacecolor'][key] = 'none'

elif key == 'Model':
plot_kwargs_defaults['linestyle'][key] = '-'
plot_kwargs_defaults['marker'][key] = None
plot_kwargs_defaults['color'][key] = 'red'
plot_kwargs_defaults['markerfacecolor'][key] = 'none'

else:
plot_kwargs_defaults['linestyle'][key] = '--'
plot_kwargs_defaults['marker'][key] = None

# Overwrite defaults with any user-provided kwargs
plot_kwargs_defaults.update(kwargs)

return pp.plot(
data_and_model,
**plot_kwargs_defaults,
)

def data_and_model_to_datagroup(
self,
energy: sc.Variable | None = None,
add_background: bool = True,
include_components: bool = True,
) -> sc.DataGroup:
"""
Create a scipp DataGroup containing the experimental data, model calculation, and
optionally the individual components.

Parameters
----------
energy : sc.Variable | None, default=None
Optional energy grid to use for the model calculation. If None, the energy grid from
the experiment is used.
add_background : bool, default=True
Whether to add the background to the model prediction when plotting individual
components.
include_components : bool, default=True
Whether to include the individual components of the model in the DataGroup. If True,
the DataGroup will include a DataArray for each component with the component's display
name as the key

Raises
------
ValueError
If no data is available in the experiment to include in the DataGroup. If no Q values
are available in the experiment to create the DataGroup. If Q_index is not set to
create the DataGroup.
TypeError
If add_background is not a boolean. If include_components is not a boolean.

Returns
-------
sc.DataGroup
A DataGroup containing the experimental data, model calculation, and optionally the
individual components.
"""

if self.experiment.binned_data is None:
raise ValueError('No data to plot. Please load data first.')
raise ValueError('No data to include in DataGroup. Please load data first.')

if self.Q is None:
raise ValueError(
'No Q values available for creating DataGroup. Please check the experiment data.'
)

if not isinstance(add_background, bool):
raise TypeError('add_background must be True or False.')

if not isinstance(include_components, bool):
raise TypeError('include_components must be True or False.')

if self.Q_index is None:
raise ValueError('Q_index must be set to create DataGroup.')

energy = self._verify_energy(energy)

if energy is None:
energy = self._masked_energy

# Create a dataset containing the data, model, and individual
# components for plotting.
data_and_model = {
'Data': self.experiment.binned_data['Q', self.Q_index],
'Model': self._create_model_array(energy=energy),
Comment thread
rozyczko marked this conversation as resolved.
}

if plot_components:
if include_components:
components = self._create_components_dataset_single_Q(
add_background=add_background, energy=energy
add_background=add_background,
energy=energy,
)
for comp_name in components:
data_and_model[comp_name] = components[comp_name]
plot_kwargs_defaults['linestyle'][comp_name] = '--'
plot_kwargs_defaults['marker'][comp_name] = None

# Overwrite defaults with any user-provided kwargs
plot_kwargs_defaults.update(kwargs)
for key in components:
data_and_model[key] = components[key]

return pp.plot(
data_and_model,
**plot_kwargs_defaults,
)
return sc.DataGroup(data_and_model)

def fix_energy_offset(self) -> None:
"""Fix the energy offset parameter for the current Q index."""
Expand Down Expand Up @@ -385,30 +457,6 @@ def _on_Q_index_changed(self) -> None:
self._masked_energy = masked_energy
self._convolver = self._create_convolver()

def _verify_energy(self, energy: sc.Variable | None) -> sc.Variable | None:
"""
Verify that the provided energy is the correct type.

Parameters
----------
energy : sc.Variable | None
The energy to verify.

Raises
------
TypeError
If energy is not a sc.Variable or None.

Returns
-------
sc.Variable | None
The verified energy, or None if no energy is provided.
"""

if energy is not None and not isinstance(energy, sc.Variable):
raise TypeError(f'Energy must be a sc.Variable or None. Got {type(energy)}.')
return energy

def _calculate_energy_with_offset(
self,
energy: sc.Variable,
Expand Down
Loading
Loading