-
Notifications
You must be signed in to change notification settings - Fork 1
Implement Perrichon2022 McStas TOSCA function as lookup table #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ajjackson
wants to merge
10
commits into
main
Choose a base branch
from
perrichon
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
81d1002
Initial implementation of scaled lookup table for TOSCA
ajjackson d4ada65
Update src/resolution_functions/models/lookuptables.py
ajjackson 5ad536e
Tidy up
ajjackson 0a4661e
Flesh out Perrichon citation
ajjackson 0ebcee5
Modernise: omega_q becomes "points"
ajjackson 16f2eb2
Move imports to top of module
ajjackson feaf728
Ruff format models/lookuptables.py
ajjackson 7e8efd0
s/omega_q/points/g in lookuptables.py
ajjackson 05fb35f
Further docstring tweaks from review
ajjackson ab234f4
Include mandatory fields in tosca.yml
ajjackson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| """ | ||
| A model based on interpolated lookup tables | ||
|
|
||
| All classes within are exposed for reference only and should not be instantiated directly. For | ||
| obtaining the :term:`resolution function` of an :term:`instrument`, please use the | ||
| `resolution_functions.instrument.Instrument.get_resolution_function` method. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
| import importlib | ||
| from typing import ClassVar, TYPE_CHECKING | ||
|
|
||
| import numpy as np | ||
| from numpy.polynomial import Polynomial | ||
| from scipy.interpolate import RegularGridInterpolator | ||
|
|
||
| from .model_base import InstrumentModel, ModelData | ||
| from .mixins import SimpleBroaden1DMixin | ||
|
|
||
| if TYPE_CHECKING: | ||
| from jaxtyping import Float | ||
|
|
||
|
|
||
| @dataclass(init=True, repr=True, frozen=True, slots=True, kw_only=True) | ||
| class ScaledTabulatedModelData(ModelData): | ||
| """ | ||
| Data for the `ScaledTabulatedModel` :term:`model`. | ||
|
|
||
| Attributes | ||
| ---------- | ||
| function | ||
| The name of the function, i.e. the alias for `ScaledTabulatedModel`. | ||
| citation | ||
| The citation for the model. Please use this to look up more details and cite the model. | ||
| npz | ||
| Relative path from Instrument yaml files to lookup table file | ||
| restrictions | ||
| defaults | ||
| """ | ||
|
|
||
| npz: str | ||
|
|
||
|
|
||
| class ScaledTabulatedModel(SimpleBroaden1DMixin, InstrumentModel): | ||
| """ | ||
| Model using a lookup table to model a 1D :term:`instrument`. | ||
|
|
||
| This allows non-Gaussian shapes to be produced. For smooth interpolation | ||
| and data efficiency, the x-axis is scaled in proportion to an approximated | ||
| standard deviation. This standard deviation is fitted to a polynomial | ||
| function of energy. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| model_data | ||
| The data associated with the model for a given version of a given instrument. | ||
|
|
||
| Attributes | ||
| ---------- | ||
| input | ||
| The names of the columns in the ``points`` array expected by all computation methods, i.e. | ||
| the names of the independent variables ([Q, w]) that the model models. | ||
| data_class | ||
| Reference to the `ScaledTabulatedModelData` type. | ||
| npz | ||
| The .npz file containing the model data | ||
| citation | ||
| """ | ||
|
|
||
| input = ("energy_transfer",) | ||
|
|
||
| data_class: ClassVar[type[ScaledTabulatedModelData]] = ScaledTabulatedModelData | ||
|
|
||
| def __init__(self, model_data: ScaledTabulatedModelData, **_): | ||
| super().__init__(model_data) | ||
| self.data = np.load( | ||
| importlib.resources.files("resins.instrument_data") / model_data.npz | ||
| ) | ||
|
|
||
| self.polynomial = Polynomial( | ||
| coef=self.data["coef"], | ||
| domain=self.data["domain"], | ||
| window=self.data["window"], | ||
| ) | ||
| self._interp = RegularGridInterpolator( | ||
| (self.data["energy_transfer"], self.data["kernel_energies"]), | ||
| self.data["table"], | ||
| method="linear", | ||
| bounds_error=False, | ||
| fill_value=0.0, | ||
| ) | ||
|
|
||
| def get_characteristics( | ||
| self, points: Float[np.ndarray, "energy_transfer dimension=1"] | ||
| ) -> dict[str, Float[np.ndarray, "sigma"]]: | ||
| """ | ||
| Computes the broadening width at each value of energy transfer (`points`). | ||
|
|
||
| The model approximates the broadening using the Gaussian distribution, so the returned | ||
| widths are in the form of the standard deviation (sigma). | ||
|
|
||
| Parameters | ||
| ---------- | ||
| points | ||
| The energy transfer in meV at which to compute the width in sigma of the kernel. | ||
| This *must* be a ``sample`` x 1 2D array where ``sample`` is the number of energy | ||
| transfers. | ||
|
|
||
| Returns | ||
| ------- | ||
| characteristics | ||
| The characteristics of the broadening function, i.e. the Gaussian width as sigma. | ||
| """ | ||
| return {"sigma": self.polynomial(points[:, 0])} | ||
|
|
||
| def get_kernel( | ||
| self, | ||
| points: Float[np.ndarray, "sample dimension=1"], | ||
| mesh: Float[np.ndarray, "mesh"], | ||
| ) -> Float[np.ndarray, "sample mesh"]: | ||
| """Computes a zero-centered broadening kernel at each value of energy transfer. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| points | ||
| The energy transfer or momentum scalar for which to compute the | ||
| kernel. This *must* be a Nx1 2D array where N is the number of w/Q | ||
| values. | ||
| mesh | ||
| The mesh on which to evaluate the kernel. A 1D array. | ||
|
|
||
| Returns | ||
| ------- | ||
| kernel | ||
| The broadening kernel at each value of `points` as given by this | ||
| model, computed on the `mesh` and centered on zero. This is a 2D N | ||
| x M array where N is the number of w/Q values and M is the length | ||
| of the `mesh` array. | ||
|
|
||
| """ | ||
|
|
||
| assert len(points.shape) == 2 and points.shape[1] == 1 | ||
| energy = points | ||
|
|
||
| scale_factors = self.polynomial(energy) | ||
| scaled_x_values = mesh / scale_factors | ||
|
|
||
| # Clip lookup energies to known maximum; width scaling should give a | ||
| # reasonable extrapolation from there | ||
| energy = np.minimum(energy, max(self.data["energy_transfer"])) | ||
|
|
||
| energy_expanded = np.meshgrid(energy[:, None], mesh, indexing="ij")[0] | ||
| lookup_mesh = np.stack([energy_expanded, scaled_x_values], axis=-1) | ||
| interp_kernels = self._interp(lookup_mesh) / scale_factors | ||
| return interp_kernels | ||
|
|
||
| def get_peak( | ||
| self, | ||
| points: Float[np.ndarray, "sample dimension=1"], | ||
| mesh: Float[np.ndarray, "mesh"], | ||
| ) -> Float[np.ndarray, "sample mesh"]: | ||
| """ | ||
| Computes the broadening kernel at each value of energy transfer. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| points | ||
| The energy transfer in meV for which to compute the kernel. This | ||
| *must* be a Nx1 2D array where N is the number of energy transfers. | ||
| mesh | ||
| The mesh on which to evaluate the kernel. This is a 1D array which | ||
| *must* span the `points` transfer space of interest. | ||
|
|
||
| Returns | ||
| ------- | ||
| kernel | ||
| The broadening kernel at each value of `points` as given by this | ||
| model, computed on the `mesh` and centered on the corresponding | ||
| energy transfer. This is a 2D N x M array where N is the number of | ||
| w/Q values and M is the length of the `mesh` array. | ||
| """ | ||
|
|
||
| shifted_meshes = [mesh - energy for energy in points[:, 0]] | ||
|
|
||
| shifted_kernels = [ | ||
| self.get_kernel(np.array([point]), shifted_mesh) | ||
| for point, shifted_mesh in zip(points, shifted_meshes) | ||
| ] | ||
|
|
||
| return np.array(np.vstack(shifted_kernels)) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.