-
Notifications
You must be signed in to change notification settings - Fork 125
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
Advanced Options for Single Crystal Elastic mode of DNS Reduction GUI #37915
base: main
Are you sure you want to change the base?
Advanced Options for Single Crystal Elastic mode of DNS Reduction GUI #37915
Conversation
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.
General observations:
-
If I click on the
Generate script
button under theScript generator
tab, then paste the script into the main window of Mantid, then run it, the data visualised under thePlotting
tab is no longer meaningful.
-
This button should be labelled
Generate and run script
. I did not realise that it was actually running the script.
-
If I don't click on the
Options
tab during the workflow, I get an arcane message about not setting the binning, when all I needed to do was click on theOptions
tab. This does not seem like user-friendly behaviour. -
This widget is a bit counter-intuitive. If I edit
2theta min
the hit tab, I'm taken to theomega min
box instead of2theta max
. This may just be a personal thing, but I feel like the the 2theta controls should be in a separate widget from the omega controls because it's a little difficult to visually parse.
h, k, l = hkl | ||
alpha = radians(alpha) | ||
beta = radians(beta) | ||
gamma = radians(gamma) | ||
cos_alpha = cos(alpha) | ||
cos_beta = cos(beta) | ||
cos_gamma = cos(gamma) | ||
sin_alpha = sin(alpha) | ||
sin_beta = sin(beta) | ||
sin_gamma = sin(gamma) | ||
vol = a * b * c * sqrt(1.0 - cos_alpha**2 - cos_beta**2 - cos_gamma**2 + 2.0 * cos_alpha * cos_beta * cos_gamma) | ||
s11 = b**2 * c**2 * sin_alpha**2 | ||
s22 = a**2 * c**2 * sin_beta**2 | ||
s33 = a**2 * b**2 * sin_gamma**2 | ||
s12 = a * b * c**2 * (cos_alpha * cos_beta - cos_gamma) | ||
s23 = a**2 * b * c * (cos_beta * cos_gamma - cos_alpha) | ||
s13 = a * b**2 * c * (cos_gamma * cos_alpha - cos_beta) | ||
inverse_d_square = 1.0 / vol**2 * (s11 * h**2 + s22 * k**2 + s33 * l**2 + 2.0 * s12 * h * k + 2 * s23 * k * l + 2 * s13 * h * l) | ||
d = sqrt(1.0 / inverse_d_square) |
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.
This can be reduced to 2 lines of linear algebra assuming you have access to the reciprocal basis b, with maybe 1 extra function call to convert the lattice parameters to a matrix. I'm sure that these functions must exist somewhere in Mantid.
q = (hkl) x b^T (vector hkl multiplied by transpose reciprocal basis)
d = 1 / |q|
This would be much less error prone than reproducing all of the equations in terms of angles.
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.
Nice suggestion, thanks! It turns out that the UnitCell class in mantid.geometry has a method to calculate d out of hkl and lattice parameters. I updated this part correspondingly.
def convert_hkl_string(hkl): | ||
# catches if user uses brackets or spaces in hkl specification | ||
hkl = hkl.strip("[]()") | ||
hkl = hkl.replace(" ", ",") | ||
return hkl |
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.
Instead of ensuring that the strings passed to the core function are correct, you could just not allow the user to enter anything other than 3 integers (e.g. 3 QSpinBox
widgets). This may not be appropriate if you forsee the user having to enter a lot of (hkl) triples.
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.
Good point, thanks. I decided to keep the input as a string, but included a QRegExpValidator in elastic_single_crystal_options_view.py that makes sure that only allowed pattern can be entered
binning_param_dict = {} | ||
binning_param_dict["two_theta"] = get_automatic_two_theta_binning(sample_data) | ||
binning_param_dict["omega"] = get_automatic_omega_binning(sample_data) |
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.
The name binning_param_dict
seems a bit misleading since it contains histogram bin edges, which I would consider to be data rather than parameters.
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.
Thanks! I've refactored it
for parameter in binning_param_dict[key].keys(): | ||
own_options[parameter] = binning_param_dict[key][parameter] | ||
|
||
def _set_min_max_binning_lims(self, sample_data): |
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.
min
and max
make this name unneccessarily long (they are implied by the lim
part)
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.
thanks, I've refactored it
fields.append(field) | ||
angle_and_fields_data[det_bank] = set(fields) | ||
return angle_and_fields_data | ||
unique_fields = list(set(fields)) |
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.
What data type does fields
contain? Does this type have a reasonable equality operator for the purposes of set
?
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.
not sure if I understand what you mean here. fields
is a list of strings, and I used set() to keep only unique values (drop duplicates)
self._treeview.blockSignals(True) | ||
self._treeview.collapseAll() | ||
self._treeview.blockSignals(False) |
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.
I think the best practice is to use a scoped QSignalBlocker
rather than a pair of calls to blockSignals
.
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.
thanks, I improved this part
try: | ||
vana = mtd[vana_name] | ||
vana_norm = mtd[vana_norm] | ||
except KeyError: | ||
raise_error(f"No vanadium file for field {field_name}.") | ||
return mtd[workspace_name] |
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.
I prefer something like dict.get(some_key, some_default)
to using exceptions as flow control. some_default
is by default None
, so you can also use if dict.get(some_key)
...`.
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.
this won't work here because mtd is not a dictionary, but rather a call to a workspace available (or not) in the list of created workspaces (see https://archive.mantidproject.org/Accessing_Workspaces_From_Python for ref). I modified the code using mtd.doesExist() so it works in a similar way you're suggesting, i.e. using if...else instead
@@ -645,7 +645,7 @@ | |||
<bool>false</bool> | |||
</property> | |||
<property name="maximum"> | |||
<double>150.000000000000000</double> |
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.
Is there a reason for this level of numerical precision in this (xml
?) file?
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.
not really, I just updated the value using QtDesigner, and for some reason that is a default format there
self._set_auto_omega_binning() | ||
selected_data = self.param_dict["file_selector"]["full_data"] | ||
if selected_data: | ||
get_wavelength_from_data_is_on = self._get_wavelength_from_data_state() |
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.
No real need to define this variable (see below, also). Why not just call the function directly from the if
statement. If the issue is clarity, then why not rename the function to the name of this variable?
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.
I updated the code correspondingly
if not standard: | ||
LoadDNSSCD( | ||
FileNames=filepaths, | ||
OutputWorkspace=workspace_name, | ||
NormalizationWorkspace=norm_name, | ||
Normalization=params["norm_to"], | ||
a=params["a"], | ||
b=params["b"], | ||
c=params["c"], | ||
alpha=params["alpha"], | ||
beta=params["beta"], | ||
gamma=params["gamma"], | ||
OmegaOffset=params["omega_offset"], | ||
HKL1=params["hkl1"], | ||
HKL2=params["hkl2"], | ||
LoadAs="raw", | ||
SaveHuberTo=f"huber_{field_name}", | ||
) | ||
else: | ||
try: | ||
LoadDNSSCD( | ||
FileNames=filepaths, | ||
OutputWorkspace=workspace_name, | ||
NormalizationWorkspace=norm_name, | ||
Normalization=params["norm_to"], | ||
a=params["a"], | ||
b=params["b"], | ||
c=params["c"], | ||
alpha=params["alpha"], | ||
beta=params["beta"], | ||
gamma=params["gamma"], | ||
OmegaOffset=params["omega_offset"], | ||
HKL1=params["hkl1"], | ||
HKL2=params["hkl2"], | ||
LoadAs="raw", | ||
LoadHuberFrom=f"huber_{field_name}", | ||
) | ||
# handling of std data when sample and vana fields are different but ignore | ||
# vanadium fields option is selected | ||
except ValueError: | ||
if params["ignore_vana"]: | ||
for field in sample_fields_to_try: | ||
try: | ||
LoadDNSSCD( | ||
FileNames=filepaths, | ||
OutputWorkspace=workspace_name, | ||
NormalizationWorkspace=norm_name, | ||
Normalization=params["norm_to"], | ||
a=params["a"], | ||
b=params["b"], | ||
c=params["c"], | ||
alpha=params["alpha"], | ||
beta=params["beta"], | ||
gamma=params["gamma"], | ||
OmegaOffset=params["omega_offset"], | ||
HKL1=params["hkl1"], | ||
HKL2=params["hkl2"], | ||
LoadAs="raw", | ||
LoadHuberFrom=f"huber_{field}", | ||
) |
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.
Some boilerplate here. It's very difficult to tell the difference between these three calls of the same function. Is it possible to handle these 3 cases in the LoadDNSSCD
constructor? You could pass standard
as an argument, and then catch any exceptions in the class constructor.
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.
I added process_dns_scd_raw
function, which has standard
as an argument, to handle these cases. I also removed the unnecessary declaration of lattice properties in a call of LoadDNSSCD
, since these properties are not used anyways when LoadAs
parameter is set to raw
.
Comments to the observations with screenshots:
|
Description of work
This PR contains advanced data reduction options selection capabilities for the Single Cristal Elastic mode of DNS Reduction GUI. Complete integration of the Single Cristal Elastic mode into the DNS Reduction GUI will be implemented in several steps (in order to ensure reasonable PR sizes and reduce the workload for the reviewers), and this PR represents the 5th step of the integration process.
Summary of work
Fixes Part 5 of #36407, as well as a bug reported in #37712.
To test:
cmake --build . --target docs-html
in build folder to create docs)This does not require release notes because this PR contains limited functionality. Release notes for the Single Cristal Elastic mode will be included in a subsequent PR, once all components for implementing the data reduction workflow are uploaded.
Reviewer
Please comment on the points listed below (full description).
Your comments will be used as part of the gatekeeper process, so please comment clearly on what you have checked during your review. If changes are made to the PR during the review process then your final comment will be the most important for gatekeepers. In this comment you should make it clear why any earlier review is still valid, or confirm that all requested changes have been addressed.
Code Review
Functional Tests
Does everything look good? Mark the review as Approve. A member of
@mantidproject/gatekeepers
will take care of it.Gatekeeper
If you need to request changes to a PR then please add a comment and set the review status to "Request changes". This will stop the PR from showing up in the list for other gatekeepers.