Add GP-node-discrete contour path (Phase 3.0e) - #96
Merged
Conversation
Closes the directive's five-path dispatch (per
docs/viewer/02-porting-from-apegmsh.md §4). Each element keeps its
own corner-value copies — the cross-element averaging step is
SKIPPED, so visualisations preserve field discontinuities at
element boundaries (material interfaces, plastic-hinge regions,
local-stress jumps).
Helper API (viewer/math/gauss_extrapolation.py):
- make_gp_discrete_scalars(*, gp_values_fn, element_index,
natural_coords, element_lookup) returns
Callable[[step], dict[int, dict[int, float]]] —
{element_id: {node_id: value}}. Parallel to
make_gp_nodal_scalars but skips
extrapolate_to_nodes_averaged; pinv projection from GPs to
corners is the same.
- Same multi-step batch + 0-D scalar defensive contracts as the
smooth helper. Out-of-range step raises IndexError, never wraps.
ContourLayer (viewer/layers/contour.py):
- Adds topology="nodal_discrete". Scalars contract:
dict[element_id, dict[node_id, float]] (or callable returning
one).
- Routes through the same backend.add_polygons(point_values=...)
call as topology="nodal" — the difference lives entirely in the
per-vertex lookup. Duplicated copies of a shared node hold the
element-local value instead of the averaged value.
- Topology validation extended; the unknown-mode error message
now lists all three accepted modes.
- Missing-entry error is topology-aware: "missing element id N"
when the outer key is absent, "missing entry for (element id E,
node id N)" when the inner key is absent.
Same PyVista renderer; MplBackend still raises
BackendCapabilityError on per-vertex coloring — covers both nodal
modes identically.
9 new tests:
- 7 unit tests in tests/viewer/math/test_gauss_extrapolation.py:
constant-field nested dict, two-quad per-element preserved (not
averaged), step-varying callable, empty lookup, 0-D input
rejected, multi-step batch indexing, out-of-range raises.
- 6 PyVista-gated integration tests in
tests/viewer/layers/test_contour_layer.py: discrete-topology
acceptance, mpl capability error, two-quad value preservation
at shared edges, in-place update_to_step on the same actor,
missing-element and missing-(element,node) error messages,
end-to-end via make_gp_discrete_scalars.
- 2 mpl-only tests: topology validation message lists all modes;
discrete mode on mpl raises capability error with the
"per-vertex" hint.
Local suites:
- system Python (no pyvista): tests/viewer/ overall 32 contour
tests pass + 35 gauss tests pass. Full project suite 1629 passed,
54 skipped. (+10 over the 1619 baseline; 6 new pyvista-gated
tests skip correctly.)
- opensees_venv (pyvista 0.47.1 + VTK 9.6.1): combined
contour + gauss suite 81 passed, including all 6 new PyVista
integration tests.
Phase 3 contour story is now complete. The five directive paths
all flow through ContourLayer + add_polygons + the gauss
extrapolation kernel:
| Path | Topology | Helper |
|----------------------------|------------------|------------------------------|
| nodal | nodal | (caller-supplied) |
| cell | cell | (caller-supplied) |
| GP-cell-averaged | cell | (caller pre-aggregates) |
| GP-node-extrap-smooth | nodal | make_gp_nodal_scalars |
| GP-node-discrete | nodal_discrete | make_gp_discrete_scalars |
Next: bump pyproject.toml + tag v1.11.0 / v1.12.0 release on main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the directive's five-path dispatch (per
docs/viewer/02-porting-from-apegmsh.md§4). Each element keeps its own corner-value copies — the cross-element averaging step is skipped, so visualisations preserve field discontinuities at element boundaries (material interfaces, plastic-hinge regions, local-stress jumps).What lands
make_gp_discrete_scalarsinviewer/math/gauss_extrapolation.py: parallel tomake_gp_nodal_scalarsbut skipsextrapolate_to_nodes_averaged. ReturnsCallable[[step], dict[int, dict[int, float]]]—{element_id: {node_id: value}}. Same multi-step batch + 0-D defensive contracts as the smooth helper.ContourLayer.topology="nodal_discrete": routes through the samebackend.add_polygons(point_values=...)call as"nodal"— the difference lives entirely in the per-vertex lookup. Duplicated vertex copies of a shared node hold the element-local value instead of the averaged value."missing element id N"(outer key) vs"missing entry for (element id E, node id N)"(inner key).The five paths, all done
"nodal""cell""cell""nodal"make_gp_nodal_scalars"nodal_discrete"make_gp_discrete_scalarsTest plan
update_to_stepon the SAME actor, missing-element and missing-(element,node) error messages, end-to-end viamake_gp_discrete_scalars.BackendCapabilityErrorwith the "per-vertex" hint.opensees_venv(pyvista 0.47.1 + VTK 9.6.1): combined contour + gauss suite 81 passed, including all 6 new PyVista integration tests.What's next
Phase 3 contour story is complete. Followup: bump
pyproject.toml+ tag a release onmainper CLAUDE.md's versioning policy.🤖 Generated with Claude Code