Skip to content

Conversation

@klytje
Copy link
Contributor

@klytje klytje commented Nov 22, 2025

Description

After long discussions on how to support SAXS fitting in SasView in the short term, it was decided to extend the generic scattering calculator with a 'SAXS fitting' option.

When this option is selected, the Compute button text is replaced with Generate plugin model. Loading a Nuclear data file with this option enabled circumvents the usual loading logic, and instead forwards the selected file path to AUSAXS. This serves two purposes:

  1. AUSAXS also supports mmCIF files, which is the new standard in biological scattering, and
  2. we avoid having to extend/modify the PDB loading logic with the additional information required for SAXS calculations.

Clicking the Generate plugin model will then create a new SAXS fit (<filename>) model, which is automatically selected & opened for the user in the fit panel. The SAXS data may then be loaded in & fitted.

Though a little awkward, I found this implementation to work decently well, at least for a short-term solution. In the long run, I'd like to have the following:

  1. The ability to load both the SAXS data & structure file directly into the fitting panel (see my discussion here).
  2. An optional initialization function for plugin models, which is invoked once before the actual Iq calculations start. I need this to avoid attaching the loaded structure file as a property to the Iq function. Having it float in the module namespace would make it load on SasView startup, which is also not ideal.
  3. Custom plugin model interfaces in the fit panel. There's a lot of additional options available in AUSAXS which are currently difficult/impossible to expose to the user. Though this could also be implemented through a dedicated SAXS fitting window, for (1) to work, this is required.
  4. We could potentially extend the SasView file loaders to support mmCIF & full PDB data loading, if we want to avoid relying on AUSAXS for this. Alternatively, we could continue to rely on AUSAXS to parse the files, but store the data on the SasView side. Or just keep it as is. I'm indifferent on this issue.

This PR replaces #3194.

Review Checklist:

[if using the editor, use [x] in place of [ ] to check a box]

Documentation
Do we need documentation for this? There's links to the AUSAXS paper in the generated plugin model file itself, if people are interested.

Installers

  • There is a chance this will affect the installers, if so
    • Windows installer (GH artifact) has been tested (installed and worked)
    • MacOSX installer (GH artifact) has been tested (installed and worked)
    • Wheels installer (GH artifact) has been tested (installed and worked)

Licensing (untick if necessary)

  • The introduced changes comply with SasView license (BSD 3-Clause)

@klytje klytje mentioned this pull request Nov 22, 2025
14 tasks
@klytje klytje marked this pull request as ready for review November 23, 2025 07:52
@wpotrzebowski wpotrzebowski self-requested a review December 2, 2025 14:30
Copy link
Contributor

@krzywon krzywon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested, but see my code comments.

@krzywon
Copy link
Contributor

krzywon commented Dec 17, 2025

Do not merge: This PR needs to make a minor change to what is introduced by #3544 (due to an API-breaking pyAUSAXS version update).

Is this comment in your PR description still valid, @klytje?

@klytje
Copy link
Contributor Author

klytje commented Dec 18, 2025

@krzywon No, it is ready for merging. The shape2sas calculations were different from what I remembered and will not need manual updating (I just checked).

Copy link
Contributor

@krzywon krzywon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final code review. I'll do a functionality test later.



def change_is_avg(self):
def change_computation_type(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: It's often a bad idea to completely replace a method, in case there are direct calls to it that may have been missed.

Suggested change
def change_computation_type(self):
def change_is_avg(self):
self.change_computation_type()
def change_computation_type(self):

self.checkboxPluginModel.setEnabled(self.is_avg)

# set the type of calculation
match self.cbOptionsCalc.currentIndex():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential simplification:

Suggested change
match self.cbOptionsCalc.currentIndex():
self.model.set_computation_type(self.cbOptionsCalc.currentIndex())
match self.cbOptionsCalc.currentIndex():
case 0 | 1 | 2:
pass


# plugin name base is 'SAXS fit'
# the actual model name includes a structure tag, e.g. 'SAXS fit (2epe)'
model_name = "SAXS fit"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: make this an importable constant from SAXSPluginModelGenerator and use it here and in the model, so if we ever change the plugin base name, we only have to change it in one place.

Comment on lines +1485 to +1486
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could a user arrive here and what should they do to correct it, if they do?

match self.type:
case ComputationType.SANS_2D:
if not (qy is not None and len(qy) > 0):
raise ValueError("For SANS_2D computation qy must be None or empty")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message is the opposite of what is being checked.

:param structure_path: Path to the structure file to be used by the plugin.
:return: The text of the plugin model.
"""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could remove the os import if you used pathlib.Path for everything here.

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.

4 participants