Skip to content

feat(native): solve structural frames, use the cg iteration limit, and keep no stale field - #383

Merged
lachlangrose merged 2 commits into
masterfrom
fix/native-solver-limits
Nov 2, 2026
Merged

lachlangrose merged 2 commits into
masterfrom
fix/native-solver-limits

Conversation

@lachlangrose

Copy link
Copy Markdown
Member

Three problems with solver='native' that showed up in the QGIS plugin with a real model (six faults and one stratigraphic group).

1. Structural frames (faults) can use the native solver

The coordinates 1 and 2 of a structural frame get gradient orthogonal constraints (add_orthogonal_feature → add_gradient_orthogonal_constraints). These were built as sparse rows after the setup, so check_supported refused them (add_constraints_to_least_squares), and every fault fell back to cg.

With b = 0 an orthogonal constraint is a tangent constraint (gradient · vector = 0), which the core already supports. Now, in a native setup, FiniteDifferenceInterpolator.add_gradient_orthogonal_constraints keeps the points, the vectors and the weight as raw data (_native_orthogonal, cleared by reset), and _native._add_data gives them to the core with add_tangent. A constraint with b != 0 still builds rows, so the check still refuses it.

2. The native iteration limit follows the grid

The core stops after 5000 iterations by default. cg (scipy) allows 10 × the number of unknowns. On a grid of 87 232 nodes the native solve stopped at 5000 iterations with a residual of 1.2e-6. Now, without maxiter in solver_kwargs, _core_settings uses 10 × n_nodes. With it the same model converged in 7442 iterations (4.6 s).

3. No stale field after a solve that does not converge

When a native solve did not converge, c kept its old value. If the grid changed after the last solve (for example a new number of elements), the old c had the old size, the builder still marked the feature as built, and the surfaces failed with cannot assign 51000 vlaues to array of shape 87232. Now, if the old c is missing, only NaN, or from another grid, c gets the partial field (as the matrix solvers do). A valid old field of the same grid is still kept, as before.

Tests

  • New packages/loop_interpolation/tests/test_native_orthogonal_and_limits.py: orthogonal constraints are kept as raw data and give the same field as cg (within 1e-4 of the range); b != 0 is still refused; the default limit; a non-converged solve replaces a field of another grid and keeps a field of this grid.
  • New tests/unit/modelling/test_native_fault_frame.py: all three coordinates of a fault use native and converge, and the frame matches the cg frame (largest difference 9e-5 of the range). For that fault, native takes 3.7 s and cg 12.5 s.
  • packages/loop_interpolation/tests: 655 passed. tests/unit: 709 passed (with NumPy 2.4; with NumPy 2.5 the 2D tests in test_2d_p1_p2_support.py fail because of np.cross, as noted in feat: allow loop-interpolation 0.2 and add the native extra #381).

🤖 Generated with Claude Code

…d keep no stale field

- add_gradient_orthogonal_constraints keeps the points and the vectors as
  raw data in a native setup (b = 0), and the adapter gives them to the
  core as tangent rows. So the coordinates 1 and 2 of a structural frame
  (for example a fault) can use solver='native'. Before, they raised
  NotImplementedError for add_constraints_to_least_squares.
- Without maxiter, the native iteration limit is 10 x the number of nodes,
  as the cg matrix solver. The core default (5000) stopped before it
  converged on large grids.
- When a native solve does not converge and the old field is missing,
  only NaN, or from another grid, c gets the partial field. Before, c kept
  a field of the old grid, so a later evaluation failed with
  'cannot assign N values to array of shape M'.
@lachlangrose
lachlangrose merged commit 4f8a582 into master Nov 2, 2026
53 checks passed
@lachlangrose
lachlangrose deleted the fix/native-solver-limits branch November 2, 2026 03:01
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