Skip to content

Commit a85d046

Browse files
authored
Merge pull request #1311 from UXARRAY/construct-node-edge-conn
Construct `node_edge_connectivity`
2 parents 33388f8 + a02603e commit a85d046

File tree

6 files changed

+250
-7
lines changed

6 files changed

+250
-7
lines changed
-3.97 KB
Binary file not shown.
157 KB
Loading

test/test_connectivity.py

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import pytest
2+
from pathlib import Path
3+
import uxarray as ux
4+
import numpy as np
5+
6+
from uxarray.constants import INT_DTYPE, INT_FILL_VALUE as fv
7+
8+
9+
class TestQuadHexagon:
10+
"""Test suite for the quad-hexagon mesh connectivity construction"""
11+
12+
@pytest.fixture()
13+
def uxgrid(self):
14+
grid_path = (
15+
Path(__file__).parent / "meshfiles" / "ugrid" / "quad-hexagon" / "grid.nc"
16+
)
17+
return ux.open_grid(grid_path)
18+
19+
# ─── Node Connectivity ────────────────────────────────────────────────────────────────────────────────────────────
20+
21+
def test_node_edge_connectivity(self, uxgrid):
22+
node_edges = uxgrid.node_edge_connectivity.values
23+
expected_node_edges = np.array(
24+
[
25+
[0, 1, 2],
26+
[0, 3, 4],
27+
[3, 5, fv],
28+
[5, 6, fv],
29+
[6, 7, 8],
30+
[1, 7, 9],
31+
[10, 11, fv],
32+
[10, 12, fv],
33+
[13, 14, fv],
34+
[13, 15, fv],
35+
[16, 17, fv],
36+
[16, 18, fv],
37+
[2, 11, 14],
38+
[4, 15, fv],
39+
[8, 18, fv],
40+
[9, 12, 17],
41+
],
42+
dtype=INT_DTYPE,
43+
)
44+
assert "face_node_connectivity" in uxgrid.connectivity
45+
assert "edge_node_connectivity" in uxgrid.connectivity
46+
assert "node_edge_connectivity" in uxgrid.connectivity
47+
np.testing.assert_array_equal(node_edges, expected_node_edges)
48+
49+
def test_node_face_connectivity(self, uxgrid):
50+
node_faces = uxgrid.node_face_connectivity.values
51+
expected_node_faces = np.array(
52+
[
53+
[0, 1, 2],
54+
[0, 2, fv],
55+
[0, fv, fv],
56+
[0, fv, fv],
57+
[0, 3, fv],
58+
[0, 1, 3],
59+
[1, fv, fv],
60+
[1, fv, fv],
61+
[2, fv, fv],
62+
[2, fv, fv],
63+
[3, fv, fv],
64+
[3, fv, fv],
65+
[1, 2, fv],
66+
[2, fv, fv],
67+
[3, fv, fv],
68+
[1, 3, fv],
69+
],
70+
dtype=INT_DTYPE,
71+
)
72+
assert "face_node_connectivity" in uxgrid.connectivity
73+
assert "node_face_connectivity" in uxgrid.connectivity
74+
np.testing.assert_array_equal(node_faces, expected_node_faces)
75+
76+
def test_node_node_connectivity(self, uxgrid):
77+
with pytest.raises(NotImplementedError):
78+
_ = uxgrid.node_node_connectivity.values
79+
80+
# ─── Edge Connectivity ───────────────────────────────────────────────────────────────────────────────────────────
81+
82+
def test_edge_node_connectivity(self, uxgrid):
83+
edge_nodes = uxgrid.edge_node_connectivity.values
84+
expected_edge_nodes = np.array(
85+
[
86+
[0, 1],
87+
[0, 5],
88+
[0, 12],
89+
[1, 2],
90+
[1, 13],
91+
[2, 3],
92+
[3, 4],
93+
[4, 5],
94+
[4, 14],
95+
[5, 15],
96+
[6, 7],
97+
[6, 12],
98+
[7, 15],
99+
[8, 9],
100+
[8, 12],
101+
[9, 13],
102+
[10, 11],
103+
[10, 15],
104+
[11, 14],
105+
],
106+
dtype=INT_DTYPE,
107+
)
108+
assert "face_node_connectivity" in uxgrid.connectivity
109+
assert "edge_node_connectivity" in uxgrid.connectivity
110+
np.testing.assert_array_equal(edge_nodes, expected_edge_nodes)
111+
112+
def test_edge_edge_connectivity(self, uxgrid):
113+
with pytest.raises(NotImplementedError):
114+
_ = uxgrid.edge_edge_connectivity.values
115+
116+
def test_edge_face_connectivity(self, uxgrid):
117+
edge_faces = uxgrid.edge_face_connectivity.values
118+
expected_edge_faces = np.array(
119+
[
120+
[0, 2],
121+
[0, 1],
122+
[1, 2],
123+
[0, fv],
124+
[2, fv],
125+
[0, fv],
126+
[0, fv],
127+
[0, 3],
128+
[3, fv],
129+
[1, 3],
130+
[1, fv],
131+
[1, fv],
132+
[1, fv],
133+
[2, fv],
134+
[2, fv],
135+
[2, fv],
136+
[3, fv],
137+
[3, fv],
138+
[3, fv],
139+
],
140+
dtype=INT_DTYPE,
141+
)
142+
assert "face_node_connectivity" in uxgrid.connectivity
143+
assert "edge_node_connectivity" in uxgrid.connectivity
144+
assert "edge_face_connectivity" in uxgrid.connectivity
145+
assert "face_edge_connectivity" in uxgrid.connectivity
146+
np.testing.assert_array_equal(edge_faces, expected_edge_faces)
147+
148+
# ─── Face Connectivity ───────────────────────────────────────────────────────────────────────────────────────────
149+
150+
def test_face_node_connectivity(self, uxgrid):
151+
expected_face_nodes = np.array(
152+
[
153+
[0, 1, 2, 3, 4, 5],
154+
[15, 7, 6, 12, 0, 5],
155+
[0, 12, 8, 9, 13, 1],
156+
[4, 14, 11, 10, 15, 5],
157+
],
158+
dtype=INT_DTYPE,
159+
)
160+
face_nodes = uxgrid.face_node_connectivity.values
161+
assert "face_node_connectivity" in uxgrid.connectivity
162+
assert len(uxgrid.connectivity) == 1
163+
np.testing.assert_array_equal(face_nodes, expected_face_nodes)
164+
165+
def test_face_edge_connectivity(self, uxgrid):
166+
expected_face_edges = np.array(
167+
[
168+
[0, 3, 5, 6, 7, 1],
169+
[12, 10, 11, 2, 1, 9],
170+
[2, 14, 13, 15, 4, 0],
171+
[8, 18, 16, 17, 9, 7],
172+
],
173+
dtype=INT_DTYPE,
174+
)
175+
face_edges = uxgrid.face_edge_connectivity.values
176+
assert "face_node_connectivity" in uxgrid.connectivity
177+
assert "face_edge_connectivity" in uxgrid.connectivity
178+
assert "edge_node_connectivity" in uxgrid.connectivity
179+
np.testing.assert_array_equal(face_edges, expected_face_edges)
180+
181+
def test_face_face_connectivity(self, uxgrid):
182+
face_faces = uxgrid.face_face_connectivity.values
183+
expected_face_faces = np.array(
184+
[
185+
[2, 1, 3, fv, fv, fv],
186+
[0, 2, 3, fv, fv, fv],
187+
[0, 1, fv, fv, fv, fv],
188+
[0, 1, fv, fv, fv, fv],
189+
],
190+
dtype=INT_DTYPE,
191+
)
192+
assert "face_node_connectivity" in uxgrid.connectivity
193+
assert "edge_node_connectivity" in uxgrid.connectivity
194+
assert "edge_face_connectivity" in uxgrid.connectivity
195+
assert "face_edge_connectivity" in uxgrid.connectivity
196+
assert "face_face_connectivity" in uxgrid.connectivity
197+
np.testing.assert_array_equal(face_faces, expected_face_faces)

test/test_grid.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -771,10 +771,6 @@ def test_normalize_existing_coordinates_norm_initial():
771771

772772

773773

774-
775-
776-
777-
778774
def test_from_topology():
779775
node_lon = np.array([-20.0, 0.0, 20.0, -20, -40])
780776
node_lat = np.array([-10.0, 10.0, -10.0, 10, -10])

uxarray/grid/connectivity.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,3 +453,54 @@ def _build_face_face_connectivity(grid):
453453
]
454454

455455
return face_face_connectivity
456+
457+
458+
def _populate_node_edge_connectivity(grid):
459+
"""Constructs the UGRID connectivity variable (``edge_node_connectivity``)
460+
and stores it within the internal (``Grid._ds``) and through the attribute
461+
(``Grid.edge_node_connectivity``)."""
462+
node_edge_connectivity = _build_node_edge_connectivity(
463+
grid.edge_node_connectivity.values, grid.n_node
464+
)
465+
466+
grid._ds["node_edge_connectivity"] = xr.DataArray(
467+
data=node_edge_connectivity,
468+
dims=ugrid.NODE_EDGE_CONNECTIVITY_DIMS,
469+
attrs=ugrid.NODE_EDGE_CONNECTIVITY_ATTRS,
470+
)
471+
472+
473+
@njit
474+
def _build_node_edge_connectivity(edge_nodes, n_node):
475+
"""Constructs the Node Edge Connectivity, which stores the indices of the edges that are shared by each node."""
476+
n_edge, nodes_per_edge = edge_nodes.shape
477+
478+
# count how many edges touch each node
479+
counts = np.zeros(n_node, dtype=INT_DTYPE)
480+
for e in range(n_edge):
481+
for j in range(nodes_per_edge):
482+
node = edge_nodes[e, j]
483+
if node != INT_FILL_VALUE:
484+
counts[node] += 1
485+
486+
# find the maximum
487+
max_edges = 0
488+
for i in range(n_node):
489+
if counts[i] > max_edges:
490+
max_edges = counts[i]
491+
492+
# allocate output, pad with fill
493+
node_edge = np.full((n_node, max_edges), INT_FILL_VALUE, dtype=INT_DTYPE)
494+
495+
ptr = np.zeros(n_node, dtype=INT_DTYPE)
496+
497+
# fill in
498+
for e in range(n_edge):
499+
for j in range(nodes_per_edge):
500+
node = edge_nodes[e, j]
501+
if node != INT_FILL_VALUE:
502+
idx = ptr[node]
503+
node_edge[node, idx] = e
504+
ptr[node] += 1
505+
506+
return node_edge

uxarray/grid/grid.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
_populate_face_edge_connectivity,
2929
_populate_face_face_connectivity,
3030
_populate_n_nodes_per_face,
31+
_populate_node_edge_connectivity,
3132
_populate_node_face_connectivity,
3233
)
3334
from uxarray.grid.coordinates import (
@@ -1286,9 +1287,7 @@ def edge_edge_connectivity(self, value):
12861287
def node_edge_connectivity(self) -> xr.DataArray:
12871288
"""Indices of the edges that surround each node."""
12881289
if "node_edge_connectivity" not in self._ds:
1289-
raise NotImplementedError(
1290-
"Construction of `node_edge_connectivity` not yet supported."
1291-
)
1290+
_populate_node_edge_connectivity(self)
12921291

12931292
return self._ds["node_edge_connectivity"]
12941293

0 commit comments

Comments
 (0)