Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8e5a36e
feat(plotting): add reusable plot editor controls
kewh5868 May 7, 2026
2273bcb
feat(fullrmc): expand Packmol solvent setup workflow
kewh5868 May 7, 2026
d67dab1
feat(representativefinder): add project-backed CLI and UI
kewh5868 May 7, 2026
f20c96d
feat(viewer): allow structure background color edits
kewh5868 May 7, 2026
b00a552
feat(saxs): add representative and FFT Born workflows
kewh5868 May 7, 2026
c6f4152
docs: document expanded SAXS and fullrmc workflows
kewh5868 May 7, 2026
104168a
ci: pin docformatter hook to Python 3.12
kewh5868 May 7, 2026
7b72a00
Merge upstream main into rmcsetup
kewh5868 May 15, 2026
8fd45c5
fix(mdtrajectory): preserve restart frame identity
kewh5868 May 15, 2026
82e5912
fix(cluster): defer smart shell union finalization
kewh5868 May 15, 2026
0ead7b0
feat(ui): add periodic table selector
kewh5868 May 15, 2026
8ba5e48
feat(fullrmc): support supplemental Packmol components
kewh5868 May 15, 2026
de2f384
feat(clusterdynamicsml): add run files and lifetime plots
kewh5868 May 15, 2026
03a461d
feat(batch): add project run files and queue tools
kewh5868 May 15, 2026
eefa93a
feat(pdf): expand Debyer workflow and batch queue
kewh5868 May 15, 2026
9801531
feat(saxs): add charged hard-sphere model templates
kewh5868 May 15, 2026
9ff54f3
feat(app): add workflow launchers and data overlay
kewh5868 May 15, 2026
167b114
feat(xyz2pdb): add DMSO MD reference
kewh5868 May 15, 2026
01d5408
docs: update workflow and platform guidance
kewh5868 May 15, 2026
e6d9827
fix(saxs): honor deprecated template metadata
kewh5868 May 15, 2026
256e87e
chore: ignore generated SAXS outputs
kewh5868 May 26, 2026
15808e4
feat(uvvis): add pseudo-Voigt fitting tool
kewh5868 May 26, 2026
87810c5
feat(saxs): add APS detector stitching tool
kewh5868 May 26, 2026
d4aa5bd
feat(bondanalysis): cache structure distributions
kewh5868 May 26, 2026
1731335
feat(cluster): improve trajectory extraction batching
kewh5868 May 26, 2026
2773721
feat(batch): streamline project queue setup
kewh5868 May 26, 2026
171f939
feat(saxs): expand Prefit and DREAM workflows
kewh5868 May 26, 2026
8cd7409
docs(saxs): document updated Prefit and DREAM models
kewh5868 May 26, 2026
0b4a31e
Merge origin/main into model-test
kewh5868 May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .codespell/ignore_words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ mater

;; Frobenius norm used in np.linalg.norm
fro

;; acetonitrile abbreviation used in solvent presets
acn
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ tests/contrast_fft_backend_debug_*/
tests/edm_contiguous_mode_report/
tests/representativefinder_performance/output_results/
tests/smearing_analysis_*/
tests/CsPbI3_SAXS_fit/
tests/CsPbI3_SAXS_fit.zip

# Generated reports
docs/reports/

# PyBuilder
target/
Expand Down
66 changes: 53 additions & 13 deletions docs/user-guide/preloaded-saxs-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ single paper.
| -------------------------------------------------------------- | ----------------------------------------------------------- | ---------- | --------------------------------------------- |
| `template_pydream_monosq_normalized.py` | `pyDREAM MonoSQ Normalized` | current | MonoSQ hard-sphere |
| `template_pydream_monosq_normalized_scaled_solvent.py` | `pyDREAM MonoSQ Normalized (Scaled Solvent Weight)` | current | MonoSQ hard-sphere with scale-coupled solvent |
| `template_pydream_monosq_legacy.py` | `pyDREAM MonoSQ Legacy (MDScatter)` | current | MonoSQ hard-sphere, MDScatter-compatible |
| `template_pydream_charged_monosq_normalized_scaled_solvent.py` | `pyDREAM Charged MonoSQ Normalized (Scaled Solvent Weight)` | current | MonoSQ charged hard-sphere RMSA |
| `template_pydream_poly_lma_hs.py` | `pyDREAM Poly LMA Hard-Sphere` | current | sphere-only Poly LMA hard-sphere |
| `template_pydream_poly_lma_hs_mix_approx.py` | `pyDREAM Poly LMA Hard-Sphere/Ellipsoid Mix (Approx.)` | current | mixed-shape approximate Poly LMA hard-sphere |
Expand Down Expand Up @@ -123,6 +124,10 @@ new `scale` and `offset` limits are centered around the autoscale result rather
than preserving the broad template-default ranges. The default `eff_r` starts at
3 A, the lower bound of the effective-radius search range.

The default `solv_w` range remains `[0, 1]`, but Prefit and DREAM batch
processing preserve wider user-edited bounds and the templates evaluate the
entered solvent multiplier directly.

Because the solvent branch is scale-coupled, Prefit's scale recommendation also
treats the solvent term as part of the scaled model instead of subtracting it as
an already-scaled background contribution.
Expand Down Expand Up @@ -166,6 +171,30 @@ Here \(Z\) is the charged-sphere charge in elementary-charge units,
concentration of added 1:1 electrolyte, and \(\epsilon_r\) is the solvent
relative dielectric constant.

When this template is active in Prefit, SAXSShell shows a `dielectconst`
solvent preset dropdown above the parameter table. Choosing a preset updates
the `dielectconst` table entry directly; choose `Custom` to keep or type a
sample-specific value. The built-in presets are reference-table values from
the Chemistry LibreTexts S1 solvent properties table:

| Preset | Solvent | `dielectconst` |
| ------ | ---------------------- | -------------- |
| DMF | N,N-dimethylformamide | 36.7 |
| DMSO | dimethyl sulfoxide | 47.0 |
| NMP | N-methyl-2-pyrrolidone | 32.0 |
| ACN | acetonitrile | 37.5 |
| Water | water | 78.54 |

Prefit also exposes an `Estimate` button beside the charged template's
`charge` parameter controls. It calculates the weighted formal charge from the
current component stoichiometries and weights, then writes
\(|\langle q\_{\mathrm{formal}}\rangle|\) into `charge`. The built-in formal
charge assumptions are Pb = +2, I = -1, Cs = +1, MA = +1, and FA = +1, with
common alkali/halide aliases included for related perovskite labels. Species
without an assigned formal charge are treated as neutral and reported in the
Prefit log, so neutral solvent/ligand atoms do not silently force an inorganic
oxidation state.

The implementation follows the SasView `hayter_msa` parameterization. The
template first converts the fitted parameters into SI-derived screening terms:

Expand Down Expand Up @@ -235,6 +264,8 @@ calculator targets in its metadata:
from the solution composition.
- `solv_w` receives the combined solvent-background multiplier from attenuation
and SAXS-effective solvent contrast.
- `concentration_salt` receives the computed solute species concentration in
mol/L from the loaded solution chemistry information.
- The solvent contribution is marked as globally scaled, so Prefit's autoscale
calculation treats the solvent branch as part of the model curve.

Expand All @@ -243,7 +274,7 @@ calculator targets in its metadata:
| Symbol / parameter | Meaning in SAXSShell |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| \(w_i\) | generated component weight for cluster profile \(i\) |
| \(w\_{\mathrm{solv}}\) / `solv_w` | bounded solvent contribution weight |
| \(w\_{\mathrm{solv}}\) / `solv_w` | solvent contribution multiplier with user-configured bounds |
| \(R\_{\mathrm{eff}}\) / `eff_r` | effective hard-sphere radius used in `calc_monodisperse_sq(...)`; scaled-solvent MonoSQ defaults to 3 A |
| \(\phi\_{\mathrm{vol}}\) / `vol_frac` | effective hard-sphere volume fraction inside the Percus-Yevick term |
| `scale` | global intensity scale; original MonoSQ applies it only to solute, scaled-solvent MonoSQ applies it to solute plus solvent |
Expand Down Expand Up @@ -273,6 +304,12 @@ The archived `MonoSQ Basic` template uses the same forward model but omits the
equation and simply factors the forward model into an intermediate helper
function before evaluating the likelihood.

The current `pyDREAM MonoSQ Legacy (MDScatter)` template is the supported
reproduction path for the old MDScatter notebook convention. It keeps the
unnormalized Gaussian likelihood, uses fixed \(\sigma = 10^{-4}\), applies the
solvent trace inside the global scale branch, and defaults `scale` to
\(10^{-10}\) with `vary=False`.

### Literature

- J. K. Percus and G. J. Yevick, _Analysis of Classical Statistical Mechanics by Means of Collective Coordinates_,
Expand All @@ -285,6 +322,9 @@ function before evaluating the likelihood.
- J. P. Hansen and J. B. Hayter, Molecular Physics **46**, 651-656 (1982).
- SasView `hayter_msa` charged-sphere RMSA model documentation:
<https://www.sasview.org/docs/user/models/hayter_msa.html>
- Chemistry LibreTexts, S1: Solvent Properties. Dielectric constants for DMF,
DMSO, NMP, acetonitrile, and water:
<https://chem.libretexts.org/Ancillary_Materials/Reference/Reference_Tables/Solvents/S1%3A_Solvent_Properties>

## Poly LMA Hard-Sphere

Expand Down Expand Up @@ -317,18 +357,18 @@ x_i I_i(q) S*{\mathrm{HS}}(q; R*i^{\mathrm{eff}}, \phi*{\mathrm{int}}) \\

### Variables

| Symbol / parameter | Meaning in SAXSShell |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| \(w_i\) | raw cluster-abundance coefficient generated from the project component rows |
| \(x_i\) | normalized abundance used internally by the model |
| \(R_i^{\mathrm{eff}}\) | per-cluster effective interaction radius |
| `r_eff_wN` | generated Prefit/DREAM radius parameter for cluster `wN` when sphere mode is active |
| \(\phi\_{\mathrm{solute}}\) / `phi_solute` | SAXS-effective solute interaction ratio scaling the cluster contribution |
| \(\phi\_{\mathrm{int}}\) / `phi_int` | interaction packing fraction used only inside the hard-sphere structure factor |
| \(s\_{\mathrm{solv}}\) / `solvent_scale` | bounded attenuation solvent-scaling term, used together with the `phi_solute` solvent complement |
| `scale` | solute intensity scale factor |
| `offset` | constant additive background |
| \(\sigma = e^{\log \sigma}\) / `log_sigma` | Gaussian noise scale for the DREAM likelihood |
| Symbol / parameter | Meaning in SAXSShell |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| \(w_i\) | raw cluster-abundance coefficient generated from the project component rows |
| \(x_i\) | normalized abundance used internally by the model |
| \(R_i^{\mathrm{eff}}\) | per-cluster effective interaction radius |
| `r_eff_wN` | generated Prefit/DREAM radius parameter for cluster `wN` when sphere mode is active |
| \(\phi\_{\mathrm{solute}}\) / `phi_solute` | SAXS-effective solute interaction ratio scaling the cluster contribution |
| \(\phi\_{\mathrm{int}}\) / `phi_int` | interaction packing fraction used only inside the hard-sphere structure factor |
| \(s\_{\mathrm{solv}}\) / `solvent_scale` | attenuation solvent-scaling term with user-configured bounds, used together with the `phi_solute` solvent complement |
| `scale` | solute intensity scale factor |
| `offset` | constant additive background |
| \(\sigma = e^{\log \sigma}\) / `log_sigma` | Gaussian noise scale for the DREAM likelihood |

In the current implementation, \(R_i^{\mathrm{eff}}\) is taken from the
generated parameter `r_eff_wN` when that row exists. Otherwise, the template
Expand Down
24 changes: 16 additions & 8 deletions docs/user-guide/pydream-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,28 @@ Important distinction:

## Search/Filter Presets

The DREAM tab includes three built-in presets that change both search depth and
posterior filtering behavior:

| Preset | Chains | Iterations | Burn-in | nSeedChains | Crossover burn-in | Default filter | Top % | Top N |
| --------------- | -----: | ---------: | ------: | ----------: | ----------------: | ---------------------- | ----: | ----: |
| Less Aggressive | 4 | 5000 | 15% | 24 | 500 | All Post-burnin | 20 | 1000 |
| Medium | 4 | 10000 | 20% | 40 | 1000 | All Post-burnin | 10 | 500 |
| More Aggressive | 8 | 20000 | 25% | 80 | 2000 | Top % by Log-posterior | 5 | 250 |
The DREAM tab includes built-in presets that change both search depth and
posterior filtering behavior. The first three are the current SAXSShell
profiles; the `Legacy ...` rows reproduce settings mined from the old
MDScatter pyDREAM UI and notebooks.

| Preset | Chains | Iterations | Burn-in | nSeedChains | Crossover burn-in | Default filter | Top % | Top N |
| ------------------------- | -----: | ---------: | ------: | ----------: | ----------------: | ---------------------- | ----: | ----: |
| Less Aggressive | 4 | 5000 | 15% | 24 | 500 | All Post-burnin | 20 | 1000 |
| Medium | 4 | 10000 | 20% | 40 | 1000 | All Post-burnin | 10 | 500 |
| More Aggressive | 8 | 20000 | 25% | 80 | 2000 | Top % by Log-posterior | 5 | 250 |
| Legacy GUI Default | 4 | 10000 | 20% | 40 | 1000 | All Post-burnin | 10 | 500 |
| Legacy SAXS Notebook | 50 | 15000 | 40% | 500 | 1000 | All Post-burnin | 10 | 500 |
| Legacy KWhite Long | 50 | 20000 | 40% | 500 | 1000 | All Post-burnin | 10 | 500 |
| Legacy TChaney Production | 25 | 50000 | 10% | 250 | 1000 | All Post-burnin | 10 | 500 |

Use them as follows:

- **Less Aggressive**: faster first pass, broader retained posterior
- **Medium**: balanced default for most routine runs
- **More Aggressive**: deeper search and tighter posterior screening
- **Legacy ...**: compatibility presets for reproducing older MDScatter
pyDREAM runs

If you change any of the linked controls manually, the preset switches to
`Custom`.
Expand Down
48 changes: 47 additions & 1 deletion docs/user-guide/saxs-prefit.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ All three calculators share the same composition inputs:
- solution density
- solute and solvent stoichiometries
- component molar masses
- component densities when the selected input mode requires them
- component densities for physical volume-fraction estimates and diagnostics
- beam energy
- capillary size and geometry
- beam footprint and beam profile
Expand All @@ -114,6 +114,10 @@ In practical terms, SAXSShell combines:
- linear attenuation coefficients derived from empirical formulas
- edge-resolved fluorescence yields and line families from `xraydb`
- beam-path averages defined by the selected capillary geometry
- molarity and volume-percent conventions matching
[OpenStax Chemistry 2e](https://openstax.org/books/chemistry-2e/pages/3-3-molarity)
and
[Chemistry LibreTexts](<https://chem.libretexts.org/Bookshelves/Introductory_Chemistry/Chemistry_for_Allied_Health_(Soult)/08%3A_Properties_of_Solutions/8.01%3A_Concentrations_of_Solutions>)

### Physical solute-associated volume fraction

Expand All @@ -138,6 +142,42 @@ This is closer to the concentration-plus-specific-volume logic commonly used in
solution SAXS than the older additive-volume estimate
$V_{\mathrm{solute}} / (V_{\mathrm{solute}} + V_{\mathrm{solvent}})$.

For `molarity_per_liter` inputs, SAXSShell uses a one-liter final-solution
basis: molarity defines moles of solute per liter of solution, not per liter of
neat solvent. If a solute density is available, it is the primary
volume-fraction route:

$$
V_{\mathrm{solute}} =
\frac{n_{\mathrm{solute}} M_{\mathrm{solute}}}
{\rho_{\mathrm{solute}}},
\qquad
\phi_{\mathrm{phys}} =
\frac{V_{\mathrm{solute}}}{1000\ \mathrm{cm^3}}.
$$

For the built-in `PbI2 - DMF - 0.49 M` preset, this gives
\(m*{\mathrm{PbI_2}} = 0.49 \times 461.01 = 225.8949\ \mathrm{g}\).
Using \(\rho*{\mathrm{PbI*2}} = 6.16\ \mathrm{g/mL}\) gives
\(V*{\mathrm{PbI*2}} = 36.671\ \mathrm{cm^3}\) and
\(\phi*{\mathrm{phys}} = 0.036671\) (3.6671%).

The solvent density is still useful as a consistency diagnostic. With the same
preset, \(\rho*{\mathrm{solution}} = 1.144\ \mathrm{g/mL}\) and
\(\rho*{\mathrm{DMF}} = 0.944\ \mathrm{g/mL}\) imply a neat-solvent diagnostic
volume of \(918.1051 / 0.944 = 972.569\ \mathrm{cm^3}\), and an additive
component volume ratio of 1.009240. If no solute density is supplied,
SAXSShell can fall back to solvent-density closure
\(V*{\mathrm{solute}} = V*{\mathrm{solution}} - V\_{\mathrm{solvent}}\), which
would give 0.027431 for this preset. That fallback is intentionally reported as
a different method because solution volumes are not generally additive.

The regression tests also include an independent volume-percent example from
Chemistry LibreTexts: 40 mL ethanol in 240 mL final solution is 16.7% v/v.
Converting that known composition to a molarity/density input recovers
\(\phi = 40 / 240\), which guards the calculator against confusing final
solution volume with solvent volume.

SAXSShell still prints this physical estimate in the output console for
reference. It is written to a Prefit parameter only when the active template
explicitly declares a physical volume-fraction target, such as `vol_frac` in
Expand Down Expand Up @@ -270,6 +310,12 @@ as a calculator target, so Prefit writes the physical solute-associated volume
fraction into `vol_frac` while keeping `solv_w` as the solvent-background
multiplier.

Prefit preserves user-edited bounds for `solv_w` and `solvent_scale` when
resetting states and when handing a project to DREAM batch processing. The
preloaded templates still start with conservative solvent-weight defaults, but
they do not force the solvent multiplier back into `[0, 1]` after you expand
the range.

That scaled-solvent MonoSQ template also asks Prefit to autoscale on load. If
experimental data are present and the project does not already have a saved Best
Prefit or current Prefit state for that template, Prefit applies the autoscale
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ saxshell = "saxshell.saxshell:main"
pdfsetup = "saxshell.pdfsetup:main"
representativefinder = "saxshell.representativefinder.cli:main"
structureviewer = "saxshell.saxs.structure_viewer.cli:main"
uvvisfit = "saxshell.uvvis_fitting.cli:main"
xyz2pdb = "saxshell.xyz2pdb.cli:main"

[tool.setuptools-git-versioning]
Expand Down
2 changes: 2 additions & 0 deletions src/saxshell/bondanalysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
AtomRecord,
BondAnalyzer,
BondPairDefinition,
CoordinationNumberDefinition,
)
from .presets import (
BondAnalysisPreset,
Expand Down Expand Up @@ -47,6 +48,7 @@
"BondAnalysisResultLeaf",
"BondAnalysisWorkflow",
"BondPairDefinition",
"CoordinationNumberDefinition",
"ClusterTypeSummary",
"build_plot_request",
"bondanalysis_presets_path",
Expand Down
Loading