Skip to content

NXstress I/O#967

Merged
ekapadi merged 6 commits into
neutrons:nextfrom
ekapadi:EWM12482_NeXus_prototype
May 13, 2026
Merged

NXstress I/O#967
ekapadi merged 6 commits into
neutrons:nextfrom
ekapadi:EWM12482_NeXus_prototype

Conversation

@ekapadi
Copy link
Copy Markdown
Contributor

@ekapadi ekapadi commented Mar 5, 2026

NeXus IO prototype

Overview

The objective of the NeXus IO-prototype work was to provide a first-pass
implementation of NeXus-compliant output using the existing NXstress
schema. Both output, and the corresponding input methods are now
implemented. Primarily, the classes HidraProjectFile,
HidraWorkspace, and PeakCollection are used to obtain information
about the reduced data. Supporting classes such as SampleLogs, and
InstrumentSetup are also used, where information about the instrument
and the experiment specifics is required.

A sub-objective was to provide an output format that could include all
of the data associated with an experiment, such as input-data and any
additional normalization spectra. It should be noted however, that
including this information is optional with respect to the NXstress schema.
(Also, as an alternative, it is quite common to simply specify
input-data by noting the file-names in appropriate fields. The problem with this approach
is that file-names usually will only have meaning at the originating site.)

The next sections provide a correspondence between the python classes,
and sections within the NXstress schema. Any place where there's still
confusion, or there is simply not enough information to meet the
requirements of the schema, will be indicated using bold text.

For purposes of the prototype, the nexusformat python package is used
in the implementation, and that working group's validator has been used
for validation of compliance. With respect to validation, its important
to use a validator that allows overriding NeXus base-class
definitions, which NXstress does extensively. In this regard, NeXus
International Advisory Committee's (NIAC) C-language validator is an
incomplete implementation, and gives misleading results. Also noted
were several bugs in the implementation of the nexusformat
validator. For example, this validator would not properly validate role-specified groups (noted as
UPPERCASE in the schema), which NeXus specifically allows to have any desired name.
In this case the validator actually requires UPPERCASE, and won't allow custom names.

Primary NXentry group

Issues found:

  1. start_time and end_time: These are specified as lists by
    scan-point (aka subrun number in PyRS). We could alternatively use
    the minimum and maximum over all of the sub-run times to obtain
    these values. The validator has trouble with the placement of
    lists for these fields, but technically our use of lists should
    be compliant with NXstress, and this is almost certainly an additional
    defect with the validator implementation itself

Single PEAKS group

This group is intended to contain the canonical (or reference) peak
values.

Issues found:

  1. PEAKS group: only a single PEAKS group is allowed by the
    NXstress schema. This meant that in order to include multiple
    PeakCollection we needed to use a flattened-indexing scheme.
    Such a scheme is allowed by the NXstress-schema, but it is highly
    unlikely that any generic NeXus application will be able to decode
    these indices automatically. Further, since any data reduction
    (and associated peak fitting) would normally depend on the mask
    used, a *mask* field has been added to ``PeakCollection``.

    In order to not overly modify the existing code, this mask field
    is optional, with a default value corresponding to the default
    mask.
  2. Converting from ``PyRS`` <peak tag> format to <phase name>
    and ``(h, k, l)`` (Miller indices) tags
    . At present we make
    this conversion automatically using a regular-expression based
    parser, however this is not an ideal solution. Here it would be
    better if these values were specified explicitly by PyRS as
    separate fields in the PeakCollection.
  3. It's assumed that ``PeakCollection.d_reference`` provides the
    required values to include in this section
    .
  4. ``(sx, sy, sz)`` are included from the logs, but mostly just
    because the logs had the same variable names -- this is probably
    incorrect
    !
  5. ``(qx, qy, qz)`` are required by ``NXstress`` (,
    components of the normalized scattering vector Q in the sample
    reference frame)**. These seem to have no correspondance in the
    current PyRS codebase -- these values are initialized to NaN.

FIT (NXprocess) group

This group contains the fitting results from the selected peak-profile
/ background-function combination. In order to include results from
multiple PeakCollection, this group uses the identical
flattened-indexing scheme as the PEAKS group. Note that the PEAKS
group includes all of the field-values which define this index, and
those values are not repeated in the FIT group.

  1. The splitting of the PeakCollection fields between FIT and
    PEAKS subgroups from NXstress was a bit confusing. This needs to
    be examined carefully to determine if it is correct.
  2. Not yet in PyRS but required by the ``NXstress`` schema:
    FIT/DIFFRACTOGRAM/fit, fit_errors: these datasets should contain
    the reconstructed spectrum from the fitted model. We don't seem to
    have methods to do this yet, so these are initialized to NaN.

SAMPLE (NXsample) group

This was complicated! Again the main issue is the naming of things in
NXstress vs. the naming in the PyRS codebase.

Issues found:

  1. Using ``PointList.(vx, vy, vz)`` as the sample positions? Is
    this correct?
  2. Possible mis-match between per-scan-point logs, and logs which
    have a single value for the entire experiment
    . This still needs to
    be checked log-by-log!
  3. Where at all possible, all of the available logs have been
    included in an additional logs (NXcollection) subgroup.

INSTRUMENT (NXinstrument) group

Issues found:

  1. Mask I/O should now be fully implemented, including both detector and
    solid-angle masks. By necessity, this treatment assumes that a
    <default> mask will always exist, and this mask is created at
    output when necessary. Detector and solid-angle masks are
    distinguished by their array shape. Note that under the current
    mask-naming scheme used by PyRS, masks must have *distinct* names,
    regardless of type.
  2. The calibrated vs. uncalibrated instrument is only partially
    treated, and this will require some additional work in order to make
    sure that the treatment is correct. In PyRS itself, several bugs
    were found relating to how calibration is applied:
    specifically,
    there's nothing preventing it from being applied multiple times.
  3. Monochromator information is only partially available.
  4. There is a whole lot of room for adjustments and *corrections*
    in the instrument section!

Possible extensions

  1. A better and more complete treatment of instrument calibration, and
    of the monochromator information.
  2. Treatment of masks by type. This should almost certainly be
    changed so that it is explicit. At present, distinguishing between
    a detector or solid-angle mask depends only on array shape.

Check list for the pull request

  • I have read the [CONTRIBUTING]
  • I have read the [CODE_OF_CONDUCT]
  • I have added tests for my changes
  • I have updated the documentation accordingly

Check list for the reviewer

  • I have read the [CONTRIBUTING]
  • I have verified the proposed changes
  • best software practices
    • all internal functions have an underbar, as is python standard
    • clearly named variables (better to be verbose in variable names)
    • code comments explaining the intent of code blocks
  • All the tests are passing
  • The documentation is up to date
  • code comments added when explaining intent

Manual test for the reviewer

In addition to running the unit tests, the complete NXstress-I/O implementation can be verified using the script at tests/scripts/cis_tests/NXstress_demo_script.py. Note that this script needs to be run on analysis.sns.gov (or a system that has the /HFIR mount).

References

@ekapadi ekapadi force-pushed the EWM12482_NeXus_prototype branch 10 times, most recently from 973b4a3 to 8099203 Compare March 11, 2026 18:56
@walshmm
Copy link
Copy Markdown
Contributor

walshmm commented Apr 8, 2026

Did we ever get the cis test script for this?

@ekapadi ekapadi force-pushed the EWM12482_NeXus_prototype branch from 0d049ec to 247a73c Compare May 11, 2026 17:29
  * For an overview of these changes: please run `pixi run build-dev-docs` and then see:
    `docs/_build/developer/design/nexus/IO_prototype.html`
    (built from: `docs/developer/source/design/nexus/IO_prototype.rst`).

  * Round-trip test script: `tests/scripts/cis_tests/NXstress_demo_script.py`.

  * Also fixed `HidraProjectFile` reload so that it places the variance data at the correct location in `HidraWorkspace`.
@ekapadi ekapadi force-pushed the EWM12482_NeXus_prototype branch from b4a2257 to 4cab5d9 Compare May 11, 2026 22:12
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@walshmm
Copy link
Copy Markdown
Contributor

walshmm commented May 12, 2026

The output of the test script when I ran it (NOTE: Step 3 seemed to take a while, and slowly wrote to disk):

Step 1: Loading HidraWorkspace
  file: /SNS/users/wqp/git/PyRS/tests/data/3393_PWHT-TD.h5
[INFO] Loaded diffraction data from /SNS/users/wqp/git/PyRS/tests/data/3393_PWHT-TD.h5 includes : dict_keys([None])
LoadParameterFile-[Error] Error in execution of algorithm LoadParameterFile:
LoadParameterFile-[Error] File not found: /SNS/users/wqp/git/PyRS/.pixi/envs/default/instrument/HIDRA_Parameters.xml. Unable to parse File: in "/SNS/users/wqp/git/PyRS/.pixi/envs/default/bin/../instrument/HIDRA_Parameters.xml"
LoadParameterFile-[Error] Error in execution of algorithm LoadParameterFile:
LoadParameterFile-[Error] File not found: /SNS/users/wqp/git/PyRS/.pixi/envs/default/instrument/HIDRA_Parameters.xml. Unable to parse File: in "/SNS/users/wqp/git/PyRS/.pixi/envs/default/bin/../instrument/HIDRA_Parameters.xml"
  raw counts loaded : 145 sub-run(s)
  sub-runs loaded : 145
  wavelength      : 1.5297481902827221 Angstrom

  Saving complete HiDRA project file -> /SNS/users/wqp/git/PyRS/tests/data/3393_PWHT-TD-2026-05-12-complete.h5
  Written: /SNS/users/wqp/git/PyRS/tests/data/3393_PWHT-TD-2026-05-12-complete.h5

============================================================
Step 2: Fitting peaks with generate_FitResults_from_workspace
Fitting data
peak_tag: Fe 311
x_min: 87.599
x_max: 91.569

FitPeaks-[Warning] FitPeaks property "FindBackgroundSigma" is deprecated and will be ignored.
FitPeaks-[Warning] Output error table workspace
Fitting data
peak_tag: Fe 222
x_min: 93.544
x_max: 95.89

FitPeaks-[Warning] FitPeaks property "FindBackgroundSigma" is deprecated and will be ignored.
FitPeaks-[Warning] Output error table workspace
  PeakCollections fitted: 2
    peak_tag      : 'Fe 311'
    peak_profile  : PseudoVoigt
    background    : Linear
    peak_tag      : 'Fe 222'
    peak_profile  : PseudoVoigt
    background    : Linear

============================================================
Step 3: Writing NXstress file -> NXstress_demo_output.nxs
Log entries for sub-run start and end times are not in ISO-8601 format:
  in order to continue writing, a value of '_no_log_' will be used for all time entries!

  Written: /SNS/users/wqp/git/PyRS/NXstress_demo_output.nxs

============================================================
Step 4: Reading back from NXstress file
  sub-runs read back    : 145
  PeakCollections read  : 2
    peak_tag (read back): 'Fe222'
    peak_tag (read back): 'Fe311'

Verifying SAMPLE group ...
  [sample] log names present ...
  [sample] position log shapes (expected n=145) ...
  [sample] position log mean / variance ...
    [OK] vx mean: orig=0  back=0  rel_diff=0.00e+00
    [OK] vx var: orig=854.483  back=854.483  rel_diff=0.00e+00
    [OK] vy mean: orig=12.95  back=12.95  rel_diff=0.00e+00
    [OK] vy var: orig=57.7598  back=57.7598  rel_diff=0.00e+00
    [OK] vz mean: orig=0  back=0  rel_diff=0.00e+00

Verifying INSTRUMENT group ...
  [instrument] geometry scalars ...
    [OK] arm_length: orig=0.985  back=0.985  rel_diff=0.00e+00
    [OK] detector nrows: orig=1024  back=1024  rel_diff=0.00e+00
    [OK] detector ncols: orig=1024  back=1024  rel_diff=0.00e+00
    [OK] pixel_size_x: orig=0.000292969  back=0.000292969  rel_diff=0.00e+00
    [OK] pixel_size_y: orig=0.000292969  back=0.000292969  rel_diff=0.00e+00
  [instrument] wavelength ...
    [OK] wavelength mean: orig=1.52975  back=1.52975  rel_diff=0.00e+00

Verifying INPUT_DATA group ...
  [input_data] raw_counts entries: orig=145  back=145
    [OK] sub_run=1 detector_counts mean: orig=0.144448  back=0.144448  rel_diff=0.00e+00
    [OK] sub_run=2 detector_counts mean: orig=0.167557  back=0.167557  rel_diff=0.00e+00
    [OK] sub_run=3 detector_counts mean: orig=0.172935  back=0.172935  rel_diff=0.00e+00
    [OK] sub_run=4 detector_counts mean: orig=0.162262  back=0.162262  rel_diff=0.00e+00
    [OK] sub_run=5 detector_counts mean: orig=0.141763  back=0.141763  rel_diff=0.00e+00
    [OK] sub_run=6 detector_counts mean: orig=0.144561  back=0.144561  rel_diff=0.00e+00
    [OK] sub_run=7 detector_counts mean: orig=0.167725  back=0.167725  rel_diff=0.00e+00
    [OK] sub_run=8 detector_counts mean: orig=0.173512  back=0.173512  rel_diff=0.00e+00
    [OK] sub_run=9 detector_counts mean: orig=0.163857  back=0.163857  rel_diff=0.00e+00
    [OK] sub_run=10 detector_counts mean: orig=0.140876  back=0.140876  rel_diff=0.00e+00
    [OK] sub_run=11 detector_counts mean: orig=0.144753  back=0.144753  rel_diff=0.00e+00
    [OK] sub_run=12 detector_counts mean: orig=0.167644  back=0.167644  rel_diff=0.00e+00
    [OK] sub_run=13 detector_counts mean: orig=0.174851  back=0.174851  rel_diff=0.00e+00
    [OK] sub_run=14 detector_counts mean: orig=0.165201  back=0.165201  rel_diff=0.00e+00
    [OK] sub_run=15 detector_counts mean: orig=0.141359  back=0.141359  rel_diff=0.00e+00
    [OK] sub_run=16 detector_counts mean: orig=0.144588  back=0.144588  rel_diff=0.00e+00
    [OK] sub_run=17 detector_counts mean: orig=0.168522  back=0.168522  rel_diff=0.00e+00
    [OK] sub_run=18 detector_counts mean: orig=0.174065  back=0.174065  rel_diff=0.00e+00
    [OK] sub_run=19 detector_counts mean: orig=0.16417  back=0.16417  rel_diff=0.00e+00
    [OK] sub_run=20 detector_counts mean: orig=0.141031  back=0.141031  rel_diff=0.00e+00
    [OK] sub_run=21 detector_counts mean: orig=0.144983  back=0.144983  rel_diff=0.00e+00
    [OK] sub_run=22 detector_counts mean: orig=0.168437  back=0.168437  rel_diff=0.00e+00
    [OK] sub_run=23 detector_counts mean: orig=0.174432  back=0.174432  rel_diff=0.00e+00
    [OK] sub_run=24 detector_counts mean: orig=0.164161  back=0.164161  rel_diff=0.00e+00
    [OK] sub_run=25 detector_counts mean: orig=0.142034  back=0.142034  rel_diff=0.00e+00
    [OK] sub_run=26 detector_counts mean: orig=0.144444  back=0.144444  rel_diff=0.00e+00
    [OK] sub_run=27 detector_counts mean: orig=0.167503  back=0.167503  rel_diff=0.00e+00
    [OK] sub_run=28 detector_counts mean: orig=0.174535  back=0.174535  rel_diff=0.00e+00
    [OK] sub_run=29 detector_counts mean: orig=0.16464  back=0.16464  rel_diff=0.00e+00
    [OK] sub_run=30 detector_counts mean: orig=0.141544  back=0.141544  rel_diff=0.00e+00
    [OK] sub_run=31 detector_counts mean: orig=0.145236  back=0.145236  rel_diff=0.00e+00
    [OK] sub_run=32 detector_counts mean: orig=0.167524  back=0.167524  rel_diff=0.00e+00
    [OK] sub_run=33 detector_counts mean: orig=0.174486  back=0.174486  rel_diff=0.00e+00
    [OK] sub_run=34 detector_counts mean: orig=0.164392  back=0.164392  rel_diff=0.00e+00
    [OK] sub_run=35 detector_counts mean: orig=0.141973  back=0.141973  rel_diff=0.00e+00
    [OK] sub_run=36 detector_counts mean: orig=0.145052  back=0.145052  rel_diff=0.00e+00
    [OK] sub_run=37 detector_counts mean: orig=0.167241  back=0.167241  rel_diff=0.00e+00
    [OK] sub_run=38 detector_counts mean: orig=0.174282  back=0.174282  rel_diff=0.00e+00
    [OK] sub_run=39 detector_counts mean: orig=0.164206  back=0.164206  rel_diff=0.00e+00
    [OK] sub_run=40 detector_counts mean: orig=0.142063  back=0.142063  rel_diff=0.00e+00
    [OK] sub_run=41 detector_counts mean: orig=0.145072  back=0.145072  rel_diff=0.00e+00
    [OK] sub_run=42 detector_counts mean: orig=0.165611  back=0.165611  rel_diff=0.00e+00
    [OK] sub_run=43 detector_counts mean: orig=0.174365  back=0.174365  rel_diff=0.00e+00
    [OK] sub_run=44 detector_counts mean: orig=0.16518  back=0.16518  rel_diff=0.00e+00
    [OK] sub_run=45 detector_counts mean: orig=0.141644  back=0.141644  rel_diff=0.00e+00
    [OK] sub_run=46 detector_counts mean: orig=0.143056  back=0.143056  rel_diff=0.00e+00
    [OK] sub_run=47 detector_counts mean: orig=0.165899  back=0.165899  rel_diff=0.00e+00
    [OK] sub_run=48 detector_counts mean: orig=0.174858  back=0.174858  rel_diff=0.00e+00
    [OK] sub_run=49 detector_counts mean: orig=0.164495  back=0.164495  rel_diff=0.00e+00
    [OK] sub_run=50 detector_counts mean: orig=0.140924  back=0.140924  rel_diff=0.00e+00
    [OK] sub_run=51 detector_counts mean: orig=0.142781  back=0.142781  rel_diff=0.00e+00
    [OK] sub_run=52 detector_counts mean: orig=0.166793  back=0.166793  rel_diff=0.00e+00
    [OK] sub_run=53 detector_counts mean: orig=0.175792  back=0.175792  rel_diff=0.00e+00
    [OK] sub_run=54 detector_counts mean: orig=0.164422  back=0.164422  rel_diff=0.00e+00
    [OK] sub_run=55 detector_counts mean: orig=0.139717  back=0.139717  rel_diff=0.00e+00
    [OK] sub_run=56 detector_counts mean: orig=0.143401  back=0.143401  rel_diff=0.00e+00
    [OK] sub_run=57 detector_counts mean: orig=0.167449  back=0.167449  rel_diff=0.00e+00
    [OK] sub_run=58 detector_counts mean: orig=0.174808  back=0.174808  rel_diff=0.00e+00
    [OK] sub_run=59 detector_counts mean: orig=0.164757  back=0.164757  rel_diff=0.00e+00
    [OK] sub_run=60 detector_counts mean: orig=0.141083  back=0.141083  rel_diff=0.00e+00
    [OK] sub_run=61 detector_counts mean: orig=0.143494  back=0.143494  rel_diff=0.00e+00
    [OK] sub_run=62 detector_counts mean: orig=0.166393  back=0.166393  rel_diff=0.00e+00
    [OK] sub_run=63 detector_counts mean: orig=0.174988  back=0.174988  rel_diff=0.00e+00
    [OK] sub_run=64 detector_counts mean: orig=0.161274  back=0.161274  rel_diff=0.00e+00
    [OK] sub_run=65 detector_counts mean: orig=0.137813  back=0.137813  rel_diff=0.00e+00
    [OK] sub_run=66 detector_counts mean: orig=0.141766  back=0.141766  rel_diff=0.00e+00
    [OK] sub_run=67 detector_counts mean: orig=0.165865  back=0.165865  rel_diff=0.00e+00
    [OK] sub_run=68 detector_counts mean: orig=0.169088  back=0.169088  rel_diff=0.00e+00
    [OK] sub_run=69 detector_counts mean: orig=0.159416  back=0.159416  rel_diff=0.00e+00
    [OK] sub_run=70 detector_counts mean: orig=0.140169  back=0.140169  rel_diff=0.00e+00
    [OK] sub_run=71 detector_counts mean: orig=0.14417  back=0.14417  rel_diff=0.00e+00
    [OK] sub_run=72 detector_counts mean: orig=0.163874  back=0.163874  rel_diff=0.00e+00
    [OK] sub_run=73 detector_counts mean: orig=0.170846  back=0.170846  rel_diff=0.00e+00
    [OK] sub_run=74 detector_counts mean: orig=0.159272  back=0.159272  rel_diff=0.00e+00
    [OK] sub_run=75 detector_counts mean: orig=0.139864  back=0.139864  rel_diff=0.00e+00
    [OK] sub_run=76 detector_counts mean: orig=0.144773  back=0.144773  rel_diff=0.00e+00
    [OK] sub_run=77 detector_counts mean: orig=0.167952  back=0.167952  rel_diff=0.00e+00
    [OK] sub_run=78 detector_counts mean: orig=0.169966  back=0.169966  rel_diff=0.00e+00
    [OK] sub_run=79 detector_counts mean: orig=0.155039  back=0.155039  rel_diff=0.00e+00
    [OK] sub_run=80 detector_counts mean: orig=0.137348  back=0.137348  rel_diff=0.00e+00
    [OK] sub_run=81 detector_counts mean: orig=0.146049  back=0.146049  rel_diff=0.00e+00
    [OK] sub_run=82 detector_counts mean: orig=0.165884  back=0.165884  rel_diff=0.00e+00
    [OK] sub_run=83 detector_counts mean: orig=0.171531  back=0.171531  rel_diff=0.00e+00
    [OK] sub_run=84 detector_counts mean: orig=0.158111  back=0.158111  rel_diff=0.00e+00
    [OK] sub_run=85 detector_counts mean: orig=0.138279  back=0.138279  rel_diff=0.00e+00
    [OK] sub_run=86 detector_counts mean: orig=0.146821  back=0.146821  rel_diff=0.00e+00
    [OK] sub_run=87 detector_counts mean: orig=0.159718  back=0.159718  rel_diff=0.00e+00
    [OK] sub_run=88 detector_counts mean: orig=0.167793  back=0.167793  rel_diff=0.00e+00
    [OK] sub_run=89 detector_counts mean: orig=0.158235  back=0.158235  rel_diff=0.00e+00
    [OK] sub_run=90 detector_counts mean: orig=0.13608  back=0.13608  rel_diff=0.00e+00
    [OK] sub_run=91 detector_counts mean: orig=0.144811  back=0.144811  rel_diff=0.00e+00
    [OK] sub_run=92 detector_counts mean: orig=0.160084  back=0.160084  rel_diff=0.00e+00
    [OK] sub_run=93 detector_counts mean: orig=0.163485  back=0.163485  rel_diff=0.00e+00
    [OK] sub_run=94 detector_counts mean: orig=0.158399  back=0.158399  rel_diff=0.00e+00
    [OK] sub_run=95 detector_counts mean: orig=0.136617  back=0.136617  rel_diff=0.00e+00
    [OK] sub_run=96 detector_counts mean: orig=0.142184  back=0.142184  rel_diff=0.00e+00
    [OK] sub_run=97 detector_counts mean: orig=0.161761  back=0.161761  rel_diff=0.00e+00
    [OK] sub_run=98 detector_counts mean: orig=0.161572  back=0.161572  rel_diff=0.00e+00
    [OK] sub_run=99 detector_counts mean: orig=0.158003  back=0.158003  rel_diff=0.00e+00
    [OK] sub_run=100 detector_counts mean: orig=0.136587  back=0.136587  rel_diff=0.00e+00
    [OK] sub_run=101 detector_counts mean: orig=0.142928  back=0.142928  rel_diff=0.00e+00
    [OK] sub_run=102 detector_counts mean: orig=0.162766  back=0.162766  rel_diff=0.00e+00
    [OK] sub_run=103 detector_counts mean: orig=0.166529  back=0.166529  rel_diff=0.00e+00
    [OK] sub_run=104 detector_counts mean: orig=0.153981  back=0.153981  rel_diff=0.00e+00
    [OK] sub_run=105 detector_counts mean: orig=0.137485  back=0.137485  rel_diff=0.00e+00
    [OK] sub_run=106 detector_counts mean: orig=0.141989  back=0.141989  rel_diff=0.00e+00
    [OK] sub_run=107 detector_counts mean: orig=0.164391  back=0.164391  rel_diff=0.00e+00
    [OK] sub_run=108 detector_counts mean: orig=0.170988  back=0.170988  rel_diff=0.00e+00
    [OK] sub_run=109 detector_counts mean: orig=0.153996  back=0.153996  rel_diff=0.00e+00
    [OK] sub_run=110 detector_counts mean: orig=0.13623  back=0.13623  rel_diff=0.00e+00
    [OK] sub_run=111 detector_counts mean: orig=0.143632  back=0.143632  rel_diff=0.00e+00
    [OK] sub_run=112 detector_counts mean: orig=0.16699  back=0.16699  rel_diff=0.00e+00
    [OK] sub_run=113 detector_counts mean: orig=0.172778  back=0.172778  rel_diff=0.00e+00
    [OK] sub_run=114 detector_counts mean: orig=0.157381  back=0.157381  rel_diff=0.00e+00
    [OK] sub_run=115 detector_counts mean: orig=0.133133  back=0.133133  rel_diff=0.00e+00
    [OK] sub_run=116 detector_counts mean: orig=0.144832  back=0.144832  rel_diff=0.00e+00
    [OK] sub_run=117 detector_counts mean: orig=0.170228  back=0.170228  rel_diff=0.00e+00
    [OK] sub_run=118 detector_counts mean: orig=0.173052  back=0.173052  rel_diff=0.00e+00
    [OK] sub_run=119 detector_counts mean: orig=0.159575  back=0.159575  rel_diff=0.00e+00
    [OK] sub_run=120 detector_counts mean: orig=0.132218  back=0.132218  rel_diff=0.00e+00
    [OK] sub_run=121 detector_counts mean: orig=0.14614  back=0.14614  rel_diff=0.00e+00
    [OK] sub_run=122 detector_counts mean: orig=0.171312  back=0.171312  rel_diff=0.00e+00
    [OK] sub_run=123 detector_counts mean: orig=0.173876  back=0.173876  rel_diff=0.00e+00
    [OK] sub_run=124 detector_counts mean: orig=0.159675  back=0.159675  rel_diff=0.00e+00
    [OK] sub_run=125 detector_counts mean: orig=0.135423  back=0.135423  rel_diff=0.00e+00
    [OK] sub_run=126 detector_counts mean: orig=0.148445  back=0.148445  rel_diff=0.00e+00
    [OK] sub_run=127 detector_counts mean: orig=0.169594  back=0.169594  rel_diff=0.00e+00
    [OK] sub_run=128 detector_counts mean: orig=0.173701  back=0.173701  rel_diff=0.00e+00
    [OK] sub_run=129 detector_counts mean: orig=0.160461  back=0.160461  rel_diff=0.00e+00
    [OK] sub_run=130 detector_counts mean: orig=0.136857  back=0.136857  rel_diff=0.00e+00
    [OK] sub_run=131 detector_counts mean: orig=0.147104  back=0.147104  rel_diff=0.00e+00
    [OK] sub_run=132 detector_counts mean: orig=0.167754  back=0.167754  rel_diff=0.00e+00
    [OK] sub_run=133 detector_counts mean: orig=0.173373  back=0.173373  rel_diff=0.00e+00
    [OK] sub_run=134 detector_counts mean: orig=0.160721  back=0.160721  rel_diff=0.00e+00
    [OK] sub_run=135 detector_counts mean: orig=0.136876  back=0.136876  rel_diff=0.00e+00
    [OK] sub_run=136 detector_counts mean: orig=0.145855  back=0.145855  rel_diff=0.00e+00
    [OK] sub_run=137 detector_counts mean: orig=0.167739  back=0.167739  rel_diff=0.00e+00
    [OK] sub_run=138 detector_counts mean: orig=0.174341  back=0.174341  rel_diff=0.00e+00
    [OK] sub_run=139 detector_counts mean: orig=0.162294  back=0.162294  rel_diff=0.00e+00
    [OK] sub_run=140 detector_counts mean: orig=0.139569  back=0.139569  rel_diff=0.00e+00
    [OK] sub_run=141 detector_counts mean: orig=0.1443  back=0.1443  rel_diff=0.00e+00
    [OK] sub_run=142 detector_counts mean: orig=0.167386  back=0.167386  rel_diff=0.00e+00
    [OK] sub_run=143 detector_counts mean: orig=0.17445  back=0.17445  rel_diff=0.00e+00
    [OK] sub_run=144 detector_counts mean: orig=0.162323  back=0.162323  rel_diff=0.00e+00
    [OK] sub_run=145 detector_counts mean: orig=0.139758  back=0.139758  rel_diff=0.00e+00
    pixel arrays compared for 145 sub-run(s)

Verifying DIFFRACTION group ...
  [diffraction] 2θ matrix shape: orig=(145, 720)  back=(145, 720)
  [diffraction] mask keys: orig={None}  back={None}
    [OK] mask=None intensity mean: orig=276.816  back=276.816  rel_diff=3.29e-08
    [OK] mask=None intensity var: orig=8616.37  back=8616.37  rel_diff=6.43e-09

Verifying PEAKS group ...
  [peaks] 'Fe 222' ...
    [OK] 'Fe 222' d_reference mean: orig=1.03  back=1.03  rel_diff=0.00e+00
    [OK] 'Fe 222' param 'Mixing' mean: orig=0.592582  back=0.592582  rel_diff=2.30e-09
    [OK] 'Fe 222' param 'Intensity' mean: orig=88.7307  back=88.7307  rel_diff=4.08e-10
    [OK] 'Fe 222' param 'PeakCentre' mean: orig=94.8789  back=94.8789  rel_diff=0.00e+00
    [OK] 'Fe 222' param 'FWHM' mean: orig=0.410377  back=0.410377  rel_diff=0.00e+00
    [OK] 'Fe 222' param 'A0' mean: orig=823.904  back=823.904  rel_diff=0.00e+00
    [OK] 'Fe 222' param 'A1' mean: orig=-6.21582  back=-6.21582  rel_diff=0.00e+00
    [OK] 'Fe 222' error 'Mixing' mean: orig=6.07586  back=6.07586  rel_diff=0.00e+00
    [OK] 'Fe 222' error 'Intensity' mean: orig=38.9244  back=38.9244  rel_diff=6.18e-08
    [OK] 'Fe 222' error 'PeakCentre' mean: orig=0.0419196  back=0.0419196  rel_diff=0.00e+00
    [OK] 'Fe 222' error 'FWHM' mean: orig=0.201678  back=0.201678  rel_diff=0.00e+00
    [OK] 'Fe 222' error 'A0' mean: orig=713.79  back=713.79  rel_diff=0.00e+00
    [OK] 'Fe 222' error 'A1' mean: orig=7.57949  back=7.57949  rel_diff=0.00e+00
    fit_costs length: 145  (expected 145)
  [peaks] 'Fe 311' ...
    [OK] 'Fe 311' d_reference mean: orig=1.08  back=1.08  rel_diff=0.00e+00
    [OK] 'Fe 311' param 'Mixing' mean: orig=0.5069  back=0.5069  rel_diff=8.11e-10
    [OK] 'Fe 311' param 'Intensity' mean: orig=318.393  back=318.393  rel_diff=3.31e-09
    [OK] 'Fe 311' param 'PeakCentre' mean: orig=89.904  back=89.904  rel_diff=0.00e+00
    [OK] 'Fe 311' param 'FWHM' mean: orig=0.413957  back=0.413957  rel_diff=0.00e+00
    [OK] 'Fe 311' param 'A0' mean: orig=223.396  back=223.396  rel_diff=0.00e+00
    [OK] 'Fe 311' param 'A1' mean: orig=0.469579  back=0.469579  rel_diff=0.00e+00
    [OK] 'Fe 311' error 'Mixing' mean: orig=0.172442  back=0.172442  rel_diff=0.00e+00
    [OK] 'Fe 311' error 'Intensity' mean: orig=22.798  back=22.798  rel_diff=1.56e-08
    [OK] 'Fe 311' error 'PeakCentre' mean: orig=0.00563364  back=0.00563364  rel_diff=0.00e+00
    [OK] 'Fe 311' error 'FWHM' mean: orig=0.0176738  back=0.0176738  rel_diff=0.00e+00
    [OK] 'Fe 311' error 'A0' mean: orig=284.498  back=284.498  rel_diff=0.00e+00
    [OK] 'Fe 311' error 'A1' mean: orig=3.19272  back=3.19272  rel_diff=0.00e+00
    fit_costs length: 145  (expected 145)

============================================================
Demo completed successfully.

@walshmm
Copy link
Copy Markdown
Contributor

walshmm commented May 12, 2026

With a little ai assistance, I am able to interpret these results as meaning a successful round trip with little to no drift in the data.

I would ask that some sort of wire/flow diagram in mermaid be added to help clarify the structure of the new code, as its fairly large. If there was something to visually layout pyrs/utilities/NXstress it would be easier to digest.

Consts - pyrs/utilities/NXstress/_definitions.py
I/O of -blank- subgroup? - pyrs/utilities/NXstress/_fit.py
..........................................pyrs/utilities/NXstress/_input_data.py
..........................................pyrs/utilities/NXstress/_instrument.py
..........................................pyrs/utilities/NXstress/_peaks.py
..........................................pyrs/utilities/NXstress/_sample.py
"main" of this partition of code, the actual orchestration - pyrs/utilities/NXstress/NXstress.py
maybe the io sub-components should go in a subpackage?

and in addition to that I guess there were a couple ui changes/backend changes to support the i/o. like this peak prof util?

should these bullets have an issue associated with them?

Sorry if I missed where you explained it but whats up with these try and ignore error lines in the ui code?

@ekapadi
Copy link
Copy Markdown
Contributor Author

ekapadi commented May 13, 2026

  1. Would it be OK to spin-off the additional docs to a separate story? From my point-of-view most of this is explained by the NXstress-schema itself, but I don't mind making a tree diagram of some kind showing where everything is implemented.
  2. RE "these bullets" -- yes --these should definitely have issues. Even the requirements for the additional utilities to support the cis-test indicate several places that need additional issues spun off. Chris had suggested that we definitely need to be able to load the instrument from its IDF instead of hard-coding it.
  3. The "try and ignore" lines in the UI code were unexpectedly required. Somehow I think something got merged to the main branch (not by me) that wasn't passing all of the unit tests? As I'd mentioned at slack, I think this might have something to do with changes in the Qt / Matplotlib packages.

Considering the state of PyRS code-base itself (which isn't too hot), I'd hesitate to put anything in a sub-package.

And yes, writing the input data takes a long time, but I will note that the input data is not required by the NXstress output implementation. :)

@ekapadi ekapadi merged commit c4c348b into neutrons:next May 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants