Skip to content

feat: model step and direct interpolation (usability plan, phase 4) - #135

Open
lachlangrose wants to merge 8 commits into
feat/step-based-dockfrom
feat/model-step-direct-interpolation
Open

lachlangrose wants to merge 8 commits into
feat/step-based-dockfrom
feat/model-step-direct-interpolation

Conversation

@lachlangrose

@lachlangrose lachlangrose commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Phase 4 of the usability plan. This PR is stacked on #134 (phase 3), so the base is feat/step-based-dock. Merge #134 first.

What changes

  • One primary button replaces Initialize Model, Solve Model and Update Model Data. The text and action come from the model state (steps/build_plan.py). The problems of all visible steps show above the feature list.
  • Build recalculates stale derived data. Out-of-date basal contacts, then thicknesses, are calculated on a background task with progress (main/derived_refresh.py). A failure stops the build and shows the error. With "Calculate from geology polygons" the model reads the extracted contacts directly. The project layer is for display only.
  • Start choice in step 1: build from a geological map, or interpolate surfaces from constraints. The second hides steps 2 and 3. Saved with the state.
  • Constraint types: value, interface, gradient/normal, tangent, form line, inequality, pairwise inequality. Each row has a weight and a Z source (layer, DEM or constant) (main/constraints.py). All interpolators take the same types, so none are hidden.
  • Detach a feature that the stratigraphic column makes, to keep a fixed copy of its data. Rows that the user adds to a generated feature are now used in the build (before, they were ignored).
  • Preview one feature as isolines on the DEM, as a temporary map layer (main/preview.py). Uses contourpy, which comes with matplotlib.
  • Add Fault from a centre, strike, dip, pitch and size (main/parametric_fault.py). The sizes follow the slip, as in LoopStructural: "Length across the slip" (major_axis, along cross(normal, slip)) and "Extent along the slip" (intermediate_axis). With a pitch of 90, these are along strike and down dip. It replaces the old unused add_fault_dialog.ui with a dialog in code.

Not tested in QGIS

QGIS was not available where I wrote this. These are not run:

  • the Qt code (primary button, dialogs, feature panel, preview layer, background tasks);
  • the new QGIS tests: test_detach_feature.py (with the tests for skipped rows, the JSON round trip and the stale model), test_parametric_fault.py, and additions to test_manual_foliations.py and test_derived_data_state.py.

The unit tests pass (234) and ruff is clean. Please run the QGIS tests in CI and try the workflow by hand.

Things to check

  • A model from constraint layers only, and a direct-constraint feature in a column model (the phase 4 acceptance test).
  • A column reorder, then Build: the model uses contacts and thicknesses for the new order.
  • The sign of the slip in LoopStructural. The axes are checked against the LoopStructural fault builder and in tests/unit/test_parametric_fault.py; the QGIS test checks only that the fault surface changes sign across the fault.
  • Pairwise inequality: which group ends up above the other.
  • contourpy is in the QGIS Python.

Review fixes

  • Merged the updated feat: step-based dock (usability plan, phase 3) #134. The merge broke the build: feat: step-based dock (usability plan, phase 3) #134 changed basal_contacts_inputs() and thickness_inputs() to take no arguments. Fixed in derived_refresh.py, with unit tests (tests/unit/test_derived_refresh.py).
  • When a build calculates the contacts and the thicknesses together, the thickness calculation uses the new contacts.
  • The background thread does not change the data manager. finish() puts the contacts into the model on the GUI thread.
  • A row that the user adds to or deletes from a generated feature makes the model stale.
  • A bad added row is left out, and a message after the build names it. The other rows are used.
  • Inequality and Pairwise Inequality have no weight, because LoopStructural ignores it.

Limits

  • Detach works only for column features, not faults.
  • Parametric faults cannot be edited (delete and add again) and are not in the fault topology.
  • The preview uses evenly spaced levels, not the unit boundary values. The "surface in the 3D view" option is not built.

🤖 Generated with Claude Code

Phase 4 of the usability plan.

- One primary button replaces Initialize Model, Solve Model and Update
  Model Data. Its text and action come from the model state. The problems
  of all steps show above the feature list.
- A build calculates out-of-date basal contacts and thicknesses first, on a
  background task with progress. With "Calculate from geology polygons" the
  model reads the extracted contacts directly; the project layer is for
  display only.
- Start choice: build from a geological map, or interpolate surfaces from
  constraints (hides steps 2 and 3). Saved with the state.
- Constraint types: value, interface, gradient/normal, tangent, form line,
  inequality and pairwise inequality, each with a weight and a Z source
  (layer, DEM or constant).
- Detach a generated feature to keep a fixed copy of its data. Rows added
  to a generated feature are now used in the build.
- Preview one feature as isolines on the map.
- Add Fault from a centre, strike, dip and size.
…tep branch

# Conflicts:
#	loopstructural/gui/modelling/steps/checks.py
- Make the step titles with dict().
- Rename the unused loop variable.
- When the contacts and the thicknesses are calculated together, the
  thickness calculation uses the new contacts, not the out-of-date layer.
- The background thread does not change the data manager. finish() puts
  the contacts into the model on the GUI thread.
- Read the inputs with basal_contacts_inputs() and thickness_inputs()
  without arguments. #134 changed these functions, so the build failed.
- qgsLayerToGeoDataFrame accepts a GeoDataFrame.
- Add unit tests for the order and for the thread rule.
LoopStructural puts the major axis along cross(normal, slip) and the
intermediate axis along the slip. The dialog said along strike and down
dip, which is correct only for dip slip. The labels are now "across the
slip" and "along the slip". Add axis_vectors() and tests for the axes.
- A row that the user adds to or deletes from a generated feature makes
  the model stale, so that the next action is a rebuild.
- A bad added row is left out, and the build tells the user. Before, one
  bad row removed all the added rows of the feature.
- Inequality and Pairwise Inequality have no weight, because
  LoopStructural ignores it.
- Test the save and load of detached data through JSON.

This branch has not been deployed

No deployments
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.

1 participant