Add mesh_layout='prebuilt' for user-provided mesh graphs - #91
Add mesh_layout='prebuilt' for user-provided mesh graphs#91prajwal-tech07 wants to merge 16 commits into
Conversation
|
It seems to me that there are really two mesh layouts added here: the prebuilt and delaunay triangulation from nodes. These are quite different and should be different mesh_layouts IMO. For building mesh from a given set of nodes, it would be nice to make the solution flexible enough to also support other mesh generation methods, e.g. knn-graphs. |
|
Hi @joeloskarsson, thanks for the feedback! This PR implements issue #79, which was split alongside #78 (mesh_layout="rectilinear", PR #81) and #80 (mesh_layout="triangular") as part of a planned series — @leifdenby helped scope these three issues from the original discussion in #71. The two modes in this PR are intentionally both under mesh_layout="prebuilt":
That said, I think your point about supporting other mesh generation methods (KNN, radius graphs) for the nodes-only case is a great idea. I could make the connectivity method configurable via mesh_layout_kwargs, e.g.: This would keep both modes under "prebuilt" (since the user is still "bringing their own nodes") while making connectivity generation pluggable. Would that address your concern, or do you feel they should still be separate mesh_layout Happy to adjust the design based on your and @leifdenby's input! |
…tion Add support for triangular mesh layout based on networkx.triangular_lattice_graph. Implements flat, flat_multiscale and hierarchical triangular mesh connectivity. Closes mllam#80
- Fix wording: 'can improve' -> 'is expected to improve' message-passing - Set same axis limits across all subplots in side-by-side comparison - Set aspect ratio 1.0 on all axes in side-by-side comparison
Move coordinate-creation functions from coords.py and connectivity/triangular.py into layout/rectilinear.py and layout/triangular.py. Drop triangular-specific convenience wrappers in favour of the generic two-step API. Retain backward-compatible re-exports in coords.py and connectivity/triangular.py. Update all imports and rewrite tests to use the two-step API.
- docs/mesh_layout.ipynb: note mesh_layout is supported from v0.5.0 (was v0.4.0) - layout/__init__.py: describe layouts in plain terms instead of naming the internal networkx helpers (grid_2d_graph / triangular_lattice_graph)
…ultiscale, simplify base.py Implements the approved PR mllam#92 review comments: - layout/ now produces only undirected coordinate primitives; all DiGraph construction lives in connectivity/. Removed create_single_level_2d_triangular_mesh_graph and the layout->connectivity import (mllam#4). - Generalized create_flat_multiscale_from_coordinates to handle both layouts: rectilinear keeps the exact index-arithmetic merge (output unchanged), triangular falls back to position-based KD-tree matching. Per-layout selection uses the 'diagonal' adjacency signature and the odd-refinement-factor check now only guards the index path. Deleted create_flat_multiscale_from_triangular_coordinates (mllam#5, mllam#8). - create_single_level_2d_triangular_mesh_primitive now accepts mesh_node_spacing and computes nx/ny internally, mirroring the rectilinear primitive (mllam#6). - base.py: swapped the mesh_layout/m2m_connectivity if-nesting so m2m_connectivity is outer and the primitive function is picked by mesh_layout, removing duplicated spacing handling (mllam#7). - Dropped the no-op triangular 'pattern' special-casing; the lattice fully determines triangular connectivity (mllam#2). - Renamed the unsupported-layout test placeholder 'hexagonal' -> 'nonexistent_layout' (mllam#9). All 299 tests pass; rectilinear output is unchanged by construction.
|
Hi @leifdenby! Following up on the meeting - here's my plan for reviving this PR on top of the mesh_layout structure from #92, plus the design questions around input format and docs. Would love a sanity check before I start porting. 1. Branch strategyThis branch predates #81's merge (it carries parallel copies of those commits on the old 2. How prebuilt fits the two-step architecture
The second point is a deliberate change from this PR's old design, where a full 3. User input contractCanonical: an in-memory import networkx as nx
import numpy as np
import weather_model_graphs as wmg
# user's own mesh: e.g. vertices loaded from an ICON grid file
my_mesh = nx.Graph()
for i, (x, y) in enumerate(my_mesh_coords): # [N, 2], same coords as xy
my_mesh.add_node(i, pos=np.array([x, y]), type="mesh")
# (optionally add edges here — otherwise Delaunay builds the adjacency)
graph = wmg.create.create_all_graph_components(
xy=xy_grid,
mesh_layout="prebuilt",
mesh_layout_kwargs=dict(mesh_graph=my_mesh),
m2m_connectivity="flat",
g2m_connectivity="nearest_neighbour",
m2g_connectivity="nearest_neighbour",
)4. DocsA new jupyter-book chapter If this sounds right I'll start with the layout-module port and open the docs chapter alongside it. |
…tidy tests - Rename triangular layout primitives to drop the _triangular suffix and alias them on import in base.py (same for rectilinear) for symmetry. - Use elif mesh_layout == 'triangular' in both coordinate-creation branches and raise NotImplementedError for unsupported layouts. - Inline the triangular test helpers to use the generic two-step API and drop the redundant flat-multiscale passthrough. - Clarify the 4-star/8-star pattern-equivalence test docstring. - Fix mangled UTF arrows in the vdiff reciprocity docstring.
The pattern argument only filters cardinal vs diagonal edges, which is a rectilinear distinction. Triangular primitives have only cardinal edges, so 4-star and 8-star produce identical graphs -- the test verified a no-op.
…llam#79) The prebuilt layout takes user-supplied mesh node positions (nx.Graph with pos/type/level node attributes, or a bare [N, 2] array) and passes them through as edge-less node-cloud primitives after validation. Following the design agreed in mllam#79, no adjacency is invented in the layout step: the connectivity step builds directed mesh edges straight from the node positions (method='delaunay', the default), skipping the undirected adjacency graph entirely. The connectivity step now also validates an explicit 'pattern' against the adjacency types present in the primitive and raises with the available options instead of silently producing an empty mesh; when no pattern is given, every edge the layout produced is used (behaviour-identical for the generated layouts). intra_level for hierarchical connectivity accepts method= for edge-less primitives alongside the existing pattern=.
- accept mesh_layout_kwargs=dict(mesh_graph=...) (nx.Graph or [N, 2] ndarray); no mesh_node_spacing needed since spacing is implied by the node positions - flat: forward optional pattern/method to the connectivity step (no more hardcoded 8-star default; omitting pattern uses every layout edge, which is behaviour-identical for the generated layouts) - hierarchical: multi-level primitives split by the nodes' integer 'level' attribute (lowest = finest); grid connects to the finest level as usual - flat_multiscale with prebuilt raises NotImplementedError for now - tests: input validation, primitive creation, Delaunay connectivity (incl. 1/2-node clouds, collinear degeneracy, pattern-vs-method errors), flat + hierarchical end-to-end, and a pattern-default equivalence guard for the generated layouts
New docs chapter walks through the input contract, the flat (Delaunay) and hierarchical (level-attribute) nodes-only cases with plots, the validation errors, and how to load real mesh sources (station CSV, ICON vertices) into the node-cloud graph.
ceede3a to
2b13e5a
Compare
Upstream main added nb-clean (v4.0.1) to the pre-commit config after this branch's fork point; CI lints the merge with main, so the two notebooks this branch touches need to be clean: strip execution counts/outputs from mesh_layout.ipynb and the kernel version metadata from prebuilt_mesh.ipynb.
|
The revival is implemented and pushed, following the design we converged on in #79:
Two conscious deferrals: user-provided edges (Cases 2/4 from the sketches - raises Note: this branch is based on #92's branch, so the diff currently includes those commits - it'll shrink to just the prebuilt work once #92 merges, and I'll rebase then. |
Describe your changes
Add support for
mesh_layout="prebuilt"increate_all_graph_components, enabling users to supply their own mesh graph (e.g. ICON icosahedral, MPAS Voronoi, custom observation networks) instead of relying on library-generated meshes.Two modes are supported:
nx.DiGraphwith validated node attributes (pos,type) and edge attributes (len,vdiff). The graph is used as-is after validation.nx.Graphwith only nodes (havingposandtypeattributes). The library automatically builds connectivity edges using Delaunay triangulation (with KDTree fallback for collinear/degenerate cases).Works with all three
m2m_connectivitytypes:flat,flat_multiscale, andhierarchical.Changes across 4 files:
src/.../mesh/kinds/prebuilt.py(new, 453 lines)src/.../create/base.py(+197/-38)create_all_graph_componentsfor all 3m2m_connectivitybranchessrc/.../mesh/__init__.py(+9)tests/test_prebuilt_mesh.py(new, 2869 lines)Motivation: This is a key extensibility feature that decouples mesh topology from the graph construction pipeline, allowing researchers to use arbitrary mesh structures without modifying library internals.
Dependencies: No new dependencies — uses existing
networkx,numpy,scipy.spatial(Delaunay, KDTree).Issue Link
Closes #79
Type of change
Checklist before requesting a review
pullwith--rebaseoption if possible).Checklist for reviewers
Each PR comes with its own improvements and flaws. The reviewer should check the following:
Author checklist after completed review
reflecting type of change (add section where missing):
Checklist for assignee