-
Notifications
You must be signed in to change notification settings - Fork 1
Include wavelength component in Q resolution calculation #112
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
base: next
Are you sure you want to change the base?
Changes from all commits
3ad4c72
b920a31
cade696
17809b7
b8e5edf
72ce8c3
3df64e7
bff064f
b4e730f
e6644eb
3a235f6
66e441d
bd1998c
01a2d5e
949685d
e434281
9b19294
df24037
cdd5d61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # SCM syntax highlighting & preventing 3-way merges | ||
| pixi.lock merge=binary linguist-language=YAML linguist-generated=true |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,8 +12,11 @@ | |||||||
| import numpy as np | ||||||||
| from scipy.optimize import brentq | ||||||||
|
|
||||||||
| from lr_reduction.data_info import CoordinateSystem | ||||||||
| from lr_reduction.gravity_correction import GravityDirection, gravity_correction | ||||||||
| from lr_reduction.instrument_settings import InstrumentSettings | ||||||||
| from lr_reduction.types import MantidWorkspace | ||||||||
| from lr_reduction.user_defined_function import UserDefinedFunction | ||||||||
| from lr_reduction.utils import mantid_algorithm_exec | ||||||||
|
|
||||||||
| from . import background, dead_time_correction | ||||||||
|
|
@@ -573,7 +576,6 @@ def to_dict(self): | |||||||
| norm_run=norm_run, | ||||||||
| time=time.ctime(), | ||||||||
| dq0=dq0, | ||||||||
| dq_over_q=self.dq_over_q, | ||||||||
| sequence_number=sequence_number, | ||||||||
| sequence_id=sequence_id, | ||||||||
| q_summing=self.q_summing, | ||||||||
|
|
@@ -608,6 +610,8 @@ def specular(self, q_summing=False, tof_weighted=False, bck_in_q=False, clean=Fa | |||||||
| The reflectivity values | ||||||||
| d_refl | ||||||||
| The uncertainties in the reflectivity values | ||||||||
| dq_over_q_bins | ||||||||
| The Q resolution associated with the q_bins | ||||||||
| """ | ||||||||
| if tof_weighted: | ||||||||
| self.specular_weighted(q_summing=q_summing, bck_in_q=bck_in_q) | ||||||||
|
|
@@ -631,12 +635,13 @@ def specular(self, q_summing=False, tof_weighted=False, bck_in_q=False, clean=Fa | |||||||
| self.d_refl = self.d_refl[idx[:-1]] | ||||||||
| self.q_bins = self.q_bins[idx] | ||||||||
|
|
||||||||
| # Compute Q resolution | ||||||||
| self.dq_over_q = compute_resolution(self._ws_sc, theta=self.theta, q_summing=q_summing) | ||||||||
| # TODO: integrate wavelength component to self.dq_over_q | ||||||||
| self.q_summing = q_summing | ||||||||
|
|
||||||||
| return self.q_bins, self.refl, self.d_refl | ||||||||
| # Compute Q resolution | ||||||||
| # For now keep the dq_over_q the same length as the q_bins to ensure conversion to mid-points is the same. | ||||||||
| lambda_bin_list = 4 * np.pi * np.sin(self.theta) / self.q_bins | ||||||||
| dq_over_q_bins = compute_resolution(self._ws_sc, wl_list=lambda_bin_list, theta=self.theta, q_summing=q_summing) | ||||||||
| return self.q_bins, self.refl, self.d_refl, dq_over_q_bins | ||||||||
|
|
||||||||
| def specular_unweighted(self, q_summing=False, normalize=True): | ||||||||
| """ | ||||||||
|
|
@@ -1264,71 +1269,98 @@ def emission_time_correction(self, ws, tofs): | |||||||
| return tofs | ||||||||
|
|
||||||||
|
|
||||||||
| def compute_resolution(ws, default_dq=0.027, theta=None, q_summing=False): | ||||||||
| def compute_theta_resolution(ws: MantidWorkspace, default_dq: float=0.027, theta: float=None, q_summing: bool=False) -> tuple[float, float]: | ||||||||
| """ | ||||||||
| Compute the Q resolution from the meta data. | ||||||||
| Compute the theta component of the q resolution. | ||||||||
| Corrected to include both slits. | ||||||||
|
|
||||||||
| Parameters | ||||||||
| ---------- | ||||||||
| ws : mantid.api.Workspace | ||||||||
| Mantid workspace to extract correction meta-data from. | ||||||||
| default_dq : float | ||||||||
| Default dth/th value to use if resolution cannot be computed from logs. | ||||||||
| theta : float | ||||||||
| Scattering angle in radians | ||||||||
| Scattering angle in radians. If not provided will read ths or thi from ws. | ||||||||
| q_summing : bool | ||||||||
| If True, the pixel size will be used for the resolution | ||||||||
|
|
||||||||
| Returns | ||||||||
| ------- | ||||||||
| float | ||||||||
| The dQ/Q resolution (FWHM) | ||||||||
| tuple[float, float] | ||||||||
| The theta and dtheta values in degrees | ||||||||
| """ | ||||||||
| settings = read_settings(ws) | ||||||||
|
|
||||||||
| if theta is None: | ||||||||
| theta = abs(ws.getRun().getProperty("ths").value[0]) * np.pi / 180.0 | ||||||||
| coordinate_system = CoordinateSystem.from_workspace(ws) | ||||||||
| if coordinate_system == CoordinateSystem.EARTH_CENTERED: | ||||||||
| Theta_deg = abs(ws.getRun().getProperty("thi").value[0]) | ||||||||
| theta = np.radians(Theta_deg) | ||||||||
| else: | ||||||||
| Theta_deg = abs(ws.getRun().getProperty("ths").value[0]) | ||||||||
| theta = np.radians(Theta_deg) | ||||||||
|
|
||||||||
| if q_summing: | ||||||||
| # Q resolution is reported as FWHM, so here we consider this to be | ||||||||
| # related to the pixel width | ||||||||
| # Compute pixel contribution to angular resolution | ||||||||
| sdd = 1830 | ||||||||
| if settings.sample_detector_distance: | ||||||||
| sdd = settings.sample_detector_distance * 1000 | ||||||||
| pixel_size = 0.7 | ||||||||
| pixel_size = 0.7 # mm, default pixel size for BL4B | ||||||||
| if settings.pixel_width: | ||||||||
| pixel_size = settings.pixel_width | ||||||||
|
|
||||||||
| # All angles here in radians, assuming small angles | ||||||||
| dq_over_q = np.arcsin(pixel_size / sdd) / theta | ||||||||
| print("Q summing: %g" % dq_over_q) | ||||||||
| return dq_over_q | ||||||||
| det_res = 1.0 # mm, approximate value for detector resolution contribution | ||||||||
| # Standard deviation of a top-hat function (uniform distribution) | ||||||||
| sigma_pix = pixel_size / np.sqrt(12) | ||||||||
|
backmari marked this conversation as resolved.
|
||||||||
| sigma_y = np.sqrt((det_res ** 2 + sigma_pix ** 2)) | ||||||||
|
|
||||||||
| dtheta = np.arctan(sigma_y / sdd) # in radians | ||||||||
|
|
||||||||
| # Want to export in degrees | ||||||||
| dtheta_deg = np.degrees(dtheta) | ||||||||
| theta_deg = np.degrees(theta) | ||||||||
|
|
||||||||
| #print("Q summing: %g" % dq_over_q) ## Work out some print functions throughout. | ||||||||
| return theta_deg, dtheta_deg | ||||||||
|
|
||||||||
| # We can't compute the resolution if the value of xi is not in the logs. | ||||||||
| # Since it was not always logged, check for it here. | ||||||||
| if not ws.getRun().hasProperty("BL4B:Mot:xi.RBV"): | ||||||||
| # For old data, the resolution can't be computed, so use | ||||||||
| # the standard value for the instrument | ||||||||
| print("Could not find BL4B:Mot:xi.RBV: using supplied dQ/Q") | ||||||||
| return default_dq | ||||||||
| theta_deg = np.degrees(theta) | ||||||||
| return theta_deg, default_dq | ||||||||
|
||||||||
| return theta_deg, default_dq | |
| dtheta_deg = default_dq * theta_deg | |
| return theta_deg, dtheta_deg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@welbournR This is a good catch by copilot! Previously, when there was only a theta component, the fallback value if theta could not be computed would be dQ/Q = 0.027, but now this value is returned as the fallback dtheta, which is combined with the wavelength resolution.
What would be a reasonable default dtheta?
Copilot
AI
Feb 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compute_wavelength_resolution’s docstring still describes the old Mantid-EvaluateFunction behavior ("fitted wavelength values" and "difference between wavelength and the fit"). The new implementation simply evaluates the provided resolution function on wl_list and returns (wl_list, d_lambda), so the return-value description should be updated to match what the function actually returns (and clarify expected units/meaning of d_lambda).
Uh oh!
There was an error while loading. Please reload this page.