Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fff6563
Updated Fit panel
wpotrzebowski Dec 2, 2025
6c9cdf4
Add SASBDB exporting
wpotrzebowski Dec 5, 2025
69aa00d
Added AutoRg calculation from FreeSAS
wpotrzebowski Dec 5, 2025
fb444b1
Refactor SASBDBDataCollector to improve instrument attribute handling…
wpotrzebowski Dec 8, 2025
38caf2c
Add CorMap p-value calculation to FittingWidget for improved model ev…
wpotrzebowski Dec 8, 2025
9b68267
Add SASBDBTest to test scripts
wpotrzebowski Dec 10, 2025
8965410
Updated visualization and added warning for Guiner
wpotrzebowski Dec 12, 2025
11264dc
Added required fields indication
wpotrzebowski Dec 15, 2025
879bdef
Updated dialog
wpotrzebowski Dec 23, 2025
5085a9e
Merge branch 'main' of https://github.com/SasView/sasview into sasbdb…
wpotrzebowski Dec 23, 2025
b8a1fd7
Update Visualization
wpotrzebowski Dec 23, 2025
1aa0c38
Improved export function
wpotrzebowski Jan 7, 2026
6637f6f
Test added
wpotrzebowski Jan 8, 2026
21d1eb1
Adding Help
wpotrzebowski Jan 13, 2026
b34121d
Trying to fix ruff
wpotrzebowski Jan 20, 2026
200c4a7
Trying to fix ruff
wpotrzebowski Jan 20, 2026
d37b586
Fix code style issues flagged by ruff
wpotrzebowski Jan 20, 2026
747a3f3
Added installation script for HSC
wpotrzebowski Jan 23, 2026
9528bd5
Merge branch 'main' of https://github.com/SasView/sasview into sasbdb…
wpotrzebowski Feb 6, 2026
1a0f2fa
Updated tab order
wpotrzebowski Mar 2, 2026
142f8a3
Enhance SASBDB functionality by adding start and end point attributes…
wpotrzebowski Mar 10, 2026
a05aaf6
Merge branch 'main' of https://github.com/SasView/sasview into sasbdb…
wpotrzebowski Mar 10, 2026
6ed0578
Fixed sphere ratio
wpotrzebowski Mar 10, 2026
a7833e8
Documentation updated
wpotrzebowski Mar 20, 2026
6adb54e
Merge branch 'main' of https://github.com/SasView/sasview into sasbdb…
wpotrzebowski Mar 20, 2026
bafa577
Change order in docs
wpotrzebowski Mar 20, 2026
a41c336
Add FreeSAS Guinier estimation feature to SASBDB dialog
wpotrzebowski Apr 1, 2026
c6763e8
Enhance SASBDBDialogTest with additional sample data fields
wpotrzebowski Apr 2, 2026
326a818
Enhance SASBDB export functionality and improve unit tests
wpotrzebowski Apr 2, 2026
50f778a
Refactor SASBDBDialogTest for improved error handling and cleanup
wpotrzebowski Apr 2, 2026
e9c8596
Adding more Guiner functionality and plot
wpotrzebowski Apr 9, 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
168 changes: 168 additions & 0 deletions .cursor/rules/sasview.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
alwaysApply: true
---
# SasView Cursor Rules (AI + Cursor / Copilot guidance)

Use this as a **project-specific rulebook** for AI-assisted development in the **SasView** codebase.

> **Golden rule:** never break the build. If you do, fixing it is priority #1.

---

## 0) What repo am I in?

**SasView** is a Small Angle Scattering (SAS) analysis application with a Qt-based GUI and a Python scripting surface.

**Top-level layout you'll see in this repo** (don't guess—navigate to the right place first):

- `src/sas/` — application source (GUI + computation)
- `test/` — calculation test suite
- `docs/sphinx-docs/` — Sphinx documentation sources
- `installers/`, `build_tools/`, `.github/` — packaging/CI tooling

---

## 1) How SasView is expected to run

- **Running from a source directory is not supported** for launching the GUI; it must be installed.
- Users can launch the GUI after installation with:

- `python -m sas` (from a venv)
- or `uvx sasview` (if installed via `uv`)

### Developer environment expectations (practical)

- The repo points developers to **INSTALL.md** and **DeveloperNotes** for current setup instructions.

**Rule:** If your change touches environment/setup, align with the project's current instructions rather than "typical Python project defaults".

---

## 2) Know the main subsystems before you edit

SasView has two big "centers of gravity" for changes and tests:

1. **Computation / calculators**: the `sascalc` package
2. **GUI**: the `qtgui` package

And correspondingly, **two unit test suites** exist:

- calculation tests (in the repo-level `test/` package)
- GUI tests (in `qtgui` subpackages, in directories named `UnitTesting`)

**CI expectation:** both suites must pass on Ubuntu; Windows/Mac require calculation tests.

---

## 3) AI workflow rules (how to behave when editing SasView)

### 3.1 Always start by orienting in code
Before writing anything substantial:

- Locate the *current* class/module that owns the behavior.
- Search for existing patterns in the same package (especially in `qtgui`).
- Prefer extending an existing workflow instead of inventing a new one.

### 3.2 Keep changes tight and reviewable
- Small diffs, single responsibility.
- Do not "drive-by refactor" unrelated formatting or renames.

### 3.3 Preserve scientific meaning
- Never "simplify" calculations unless you can justify equivalence.
- If you change algorithm behavior, update documentation and add/adjust tests.

---

## 4) SasView coding standards (follow project rules, not your instincts)

These are pulled from the SasView developer wiki coding rules.

### Docstrings & documentation
- Use `"""triple double quotes"""` for docstrings; use raw docstrings for backslashes.
- Docstrings are processed by Sphinx and should use reStructuredText conventions.
- For any public function/class: document arguments, return values, side effects, exceptions, and restrictions.

### Naming
- Classes: `CamelCase`
- Variables/methods/modules: `snake_case` (underscores)
- Non-public: prefix with `_` (`_snake_case`, `_CamelCase`)
- Avoid `__dunder__` names except standard Python protocol requirements.

### Imports
- One module per `import` line.
- Avoid `from x import *` (except carefully in `__init__.py` with `__all__`).
- Import grouping order: stdlib → third-party → local, separated by blank lines.

### Exceptions
- Don't catch-and-ignore (`pass`/`print`)—handle meaningfully.
- Don't use exceptions for flow control without a very good reason.
- Never use a bare `except:`; use `except Exception:` at minimum.

### Style basics
- Follow PEP 8; 4 spaces; avoid magic numbers (use named constants); max 79-char lines (unless project tooling dictates otherwise).

### API design guidance (important for "core logic" changes)
- Prefer explicit constructor arguments with validation.
- Use `@property` for user-facing fields; avoid `__getattr__`/`__setattr__` hacks.
- Add type hints to API methods.

---

## 5) GUI rules (Qt / PySide6)

SasView uses Qt via **PySide6**.

When modifying `src/sas/qtgui/...`:

- **Never block the UI thread** with heavy calculations or I/O.
- Prefer existing thread/worker patterns already used in SasView.
- Any dialog/interaction changes should have a clear UX reason and minimal regressions.
- If you touch platform-specific behavior (file dialogs, paths), sanity-check against known PySide6 differences.

---

## 6) Models & fitting (where changes often go wrong)

- SasView integrates with **sasmodels** for scattering models and **bumps** for fitting workflows (used even in the README scripting example).
- SasView supports **plugin models**; the official docs describe how to create them and what types exist.

**Rule:** if you change anything in fitting/model plumbing, add/adjust tests and verify both:
- script-level usage (non-GUI)
- GUI path (Fitting perspective)

---

## 7) Tests: what to add, where, and what must pass

- Calculation tests: add to the repo-level `test/` suite.
- GUI tests: place under the relevant `qtgui` subpackage in a `UnitTesting/` directory.

**Rule:** new features should include tests; existing passing tests must keep passing.

---

## 8) Working with sibling repositories (common SasView dev workflow)

During development, changes may land in sibling repos like `sasmodels`, `sasdata`, and `bumps`.

If you need to test a SasView change against an unmerged sibling branch, the wiki documents a CI-based workflow by editing `.github/workflows/ci.yml` to clone specific sibling branches.

**Rule:** if your SasView fix depends on a sibling change, document it clearly in the PR and link the sibling PR(s).

---

## 9) When you (the AI) are unsure

- Prefer pointing to the authoritative sources (project wiki, docs, README) instead of guessing.
- If a rule conflicts with your generic best practices, **the project rule wins**.
- If a change might be controversial, keep the diff minimal and include rationale + tests.

---

## 10) Suggested Cursor setup

Place this file somewhere discoverable (commonly repo root), and consider a small `.cursorrules` file that points to it, e.g.

- "Follow `SasView Cursor Rules (AI + Cursor guidance)` in this repo."

(Keep that pointer file tiny; keep the real rules here.)
1 change: 1 addition & 0 deletions build_tools/hsc_deployment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apt-get -y install --no-install-recommends libegl1 libopengl0 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils xvfb libxcb-cursor0
6 changes: 5 additions & 1 deletion docs/sphinx-docs/source/user/menu_bar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ The View option allows you to:

Tools
-----
The Tools option provides access to a comprehensive range of tools and utilities. See :ref:`tools` for more information.
The Tools option provides access to a comprehensive range of tools and utilities, including:

- Export to SASBDB (for submitting data to the Small Angle Scattering Biological Data Bank)

See :ref:`tools` for more information.

Window
------
Expand Down
1 change: 1 addition & 0 deletions docs/sphinx-docs/source/user/tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ Tools & Utilities

MuMag Tool <qtgui/Utilities/MuMag/mumag_help>

SASBDB Export <qtgui/Utilities/SASBDB/sasbdb_help>
36 changes: 29 additions & 7 deletions src/sas/qtgui/Calculators/Shape2SAS/ViewerModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,31 @@ def setPlot(self, distr: ModelPointDistribution, design: ViewerPlotDesign):
colours = design.colour

for series in self.dict_series.values():
data = []
series.dataProxy().resetArray(data)
data = []
series.dataProxy().resetArray(data)

# Check if we have any data at all
if not distr.x or len(distr.x) == 0:
return

minx, maxx = min(distr.x[0]), max(distr.x[0])
miny, maxy = min(distr.y[0]), max(distr.y[0])
minz, maxz = min(distr.z[0]), max(distr.z[0])
# Find first non-empty subunit for initial bounds (first may be empty after overlap)
minx = maxx = miny = maxy = minz = maxz = None
for subunit in range(len(colours)):
try:
if (subunit >= len(distr.x)
or not hasattr(distr.x[subunit], '__len__')
or len(distr.x[subunit]) == 0):
continue
except (ValueError, TypeError):
continue

minx, maxx = min(distr.x[subunit]), max(distr.x[subunit])
miny, maxy = min(distr.y[subunit]), max(distr.y[subunit])
minz, maxz = min(distr.z[subunit]), max(distr.z[subunit])
break

if minx is None:
return

for subunit in range(len(colours)):
# Skip empty subunits - handle numpy arrays properly
Expand All @@ -198,10 +213,17 @@ def setPlot(self, distr: ModelPointDistribution, design: ViewerPlotDesign):
# Handle numpy array comparison issues
continue

series = self.dict_series[colours[subunit]]
series = self.dict_series.get(colours[subunit])
if series is None:
continue

data = []
for index in range(len(distr.x[subunit])):
data.append(QScatterDataItem(QVector3D(distr.x[subunit][index], distr.y[subunit][index], distr.z[subunit][index])))
data.append(QScatterDataItem(QVector3D(
distr.x[subunit][index],
distr.y[subunit][index],
distr.z[subunit][index]
)))

minx = min(minx, min(distr.x[subunit]))
maxx = max(maxx, max(distr.x[subunit]))
Expand Down
82 changes: 82 additions & 0 deletions src/sas/qtgui/MainWindow/GuiManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from sas.qtgui.Perspectives.Inversion.InversionPerspective import InversionWindow
from sas.qtgui.Perspectives.perspective import Perspective
from sas.qtgui.Perspectives.SizeDistribution.SizeDistributionPerspective import SizeDistributionWindow
from sas.qtgui.Plotting.PlotterData import Data1D, Data2D
from sas.qtgui.Utilities.About.About import About
from sas.qtgui.Utilities.About.Credits import Credits

Expand All @@ -57,6 +58,8 @@
from sas.qtgui.Utilities.Preferences.PreferencesPanel import PreferencesPanel
from sas.qtgui.Utilities.Reports.ReportDialog import ReportDialog
from sas.qtgui.Utilities.ResultPanel import ResultPanel
from sas.qtgui.Utilities.SASBDB.sasbdb_data_collector import SASBDBDataCollector
from sas.qtgui.Utilities.SASBDB.SASBDBDialog import SASBDBDialog

# General SAS imports
from sas.qtgui.Utilities.SasviewLogger import setup_qt_logging
Expand Down Expand Up @@ -713,6 +716,7 @@ def addTriggers(self):
self._workspace.actionCopy.triggered.connect(self.actionCopy)
self._workspace.actionPaste.triggered.connect(self.actionPaste)
self._workspace.actionReport.triggered.connect(self.actionReport)
self._workspace.actionSASBDB.triggered.connect(self.actionSASBDB)
self._workspace.actionReset.triggered.connect(self.actionReset)
self._workspace.actionExcel.triggered.connect(self.actionExcel)
self._workspace.actionLatex.triggered.connect(self.actionLatex)
Expand Down Expand Up @@ -912,7 +916,85 @@ def actionReport(self):
self.report_dialog = ReportDialog(report_data=report_data, parent=self._parent)
self.report_dialog.show()

def _sasbdb_guinier_source_data_1d(self):
"""
Return loaded 1D data for optional FreeSAS Guinier estimation in SASBDB.

Prefers the active Fitting tab dataset; otherwise the most recent 1D
item in the data manager.
"""
if self._current_perspective is not None:
fw = getattr(self._current_perspective, 'currentFittingWidget', None)
if fw is not None and fw.data is not None and isinstance(fw.data, Data1D):
return fw.data
if hasattr(self, '_data_manager'):
try:
stored = self._data_manager.stored_data
if stored:
data = list(stored.values())[-1]
if isinstance(data, Data1D):
return data
except Exception:
logger.debug("Could not resolve SASBDB Guinier source data", exc_info=True)
return None

def actionSASBDB(self):
"""
Show the SASBDB Export dialog.
"""
collector = SASBDBDataCollector()
export_data = collector.export_data

# Try to get data from current perspective (same way as Report Results)
if self._current_perspective is not None:
try:
perspective_data = self._current_perspective.getSASBDBData()
if perspective_data is not None:
export_data = perspective_data
except Exception as e:
logger.warning(f"Could not collect data from perspective: {e}")

# If no samples collected, try to get from data manager
if not export_data.samples and hasattr(self, '_data_manager'):
try:
# Get the most recently added data
stored_data = self._data_manager.stored_data
if stored_data:
# Get the last data item
data_items = list(stored_data.values())
if data_items:
data = data_items[-1]
if isinstance(data, (Data1D, Data2D)):
sample, instrument = collector.collect_from_data(data)
export_data.samples.append(sample)
if instrument:
export_data.instruments.append(instrument)
except Exception as e:
logger.warning(f"Could not collect data from data manager: {e}")

# If no samples collected, create defaults
if not export_data.samples:
# Create a default sample with defaults
sample, instrument = collector.collect_from_data(Data1D(x=[], y=[]))
sample.molecule = collector.create_default_molecule()
sample.buffer = collector.create_default_buffer()
export_data.samples.append(sample)
if instrument:
export_data.instruments.append(instrument)

# Create default project if not set
if export_data.project is None:
export_data.project = collector.create_default_project()

guinier_source_data = self._sasbdb_guinier_source_data_1d()

# Show dialog
self.sasbdb_dialog = SASBDBDialog(
export_data=export_data,
parent=self._parent,
guinier_source_data=guinier_source_data,
)
self.sasbdb_dialog.show()

def actionReset(self):
"""
Expand Down
14 changes: 14 additions & 0 deletions src/sas/qtgui/MainWindow/UI/MainWindowUI.ui
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<addaction name="actionSaveParamsAs"/>
<addaction name="separator"/>
<addaction name="actionReport"/>
<addaction name="actionSASBDB"/>
<addaction name="actionReset"/>
<addaction name="separator"/>
<addaction name="actionFreeze_Theory"/>
Expand Down Expand Up @@ -197,6 +198,7 @@
<addaction name="actionReset"/>
<addaction name="actionSave"/>
<addaction name="actionReport"/>
<addaction name="actionSASBDB"/>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
<addaction name="actionCopy"/>
Expand Down Expand Up @@ -241,6 +243,18 @@
<string>report</string>
</property>
</action>
<action name="actionSASBDB">
<property name="icon">
<iconset>
<normaloff>:/res/sasbdb.png</normaloff>:/res/sasbdb.png</iconset>
</property>
<property name="text">
<string>Export to SASBDB</string>
</property>
<property name="toolTip">
<string>Export data to SASBDB format</string>
</property>
</action>
<action name="actionUndo">
<property name="icon">
<iconset>
Expand Down
Loading
Loading