Skip to content

Commit 79a3b0f

Browse files
authored
Merge pull request #922 from davidhassell/ugrid-write
Implementation of UGRID (part 2 of 2): Write
2 parents 02e348c + 8330b72 commit 79a3b0f

File tree

6 files changed

+340
-27
lines changed

6 files changed

+340
-27
lines changed

Changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Version NEXTVERSION
33

44
**2026-??-??**
55

6+
* Write UGRID datasets with `cf.write`
7+
(https://github.com/NCAS-CMS/cf-python/issues/697)
68
* Support for HEALPix grids
79
(https://github.com/NCAS-CMS/cf-python/issues/909)
810
* New HEALPix methods: `cf.Field.healpix_info`,

cf/field.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7586,18 +7586,25 @@ def collapse(
75867586

75877587
# ---------------------------------------------------------
75887588
# Update dimension coordinates, auxiliary coordinates,
7589-
# cell measures and domain ancillaries
7589+
# cell measures, domain ancillaries, domain_topologies,
7590+
# and cell connectivities.
75907591
# ---------------------------------------------------------
75917592
for axis, domain_axis in collapse_axes.items():
75927593
# Ignore axes which are already size 1
75937594
size = domain_axis.get_size()
75947595
if size == 1:
75957596
continue
75967597

7597-
# REMOVE all cell measures and domain ancillaries
7598-
# which span this axis
7598+
# REMOVE all cell measures, domain ancillaries,
7599+
# domain_topologies, and cell connectivities which
7600+
# span this axis
75997601
c = f.constructs.filter(
7600-
filter_by_type=("cell_measure", "domain_ancillary"),
7602+
filter_by_type=(
7603+
"cell_measure",
7604+
"domain_ancillary",
7605+
"domain_topology",
7606+
"cell_connectivity",
7607+
),
76017608
filter_by_axis=(axis,),
76027609
axis_mode="or",
76037610
todict=True,

cf/test/create_test_files.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,6 +2228,150 @@ def _make_ugrid_2(filename):
22282228
return filename
22292229

22302230

2231+
def _make_ugrid_3(filename):
2232+
"""Create a UGRID mesh topology and no fields/domains."""
2233+
n = netCDF4.Dataset(filename, "w")
2234+
2235+
n.Conventions = f"CF-{VN}"
2236+
2237+
n.createDimension("nMesh3_node", 7)
2238+
n.createDimension("nMesh3_edge", 9)
2239+
n.createDimension("nMesh3_face", 3)
2240+
n.createDimension("connectivity2", 2)
2241+
n.createDimension("connectivity4", 4)
2242+
n.createDimension("connectivity5", 5)
2243+
2244+
Mesh3 = n.createVariable("Mesh3", "i4", ())
2245+
Mesh3.cf_role = "mesh_topology"
2246+
Mesh3.topology_dimension = 2
2247+
Mesh3.node_coordinates = "Mesh3_node_x Mesh3_node_y"
2248+
Mesh3.face_node_connectivity = "Mesh3_face_nodes"
2249+
Mesh3.edge_node_connectivity = "Mesh3_edge_nodes"
2250+
Mesh3.face_dimension = "nMesh3_face"
2251+
Mesh3.edge_dimension = "nMesh3_edge"
2252+
Mesh3.face_face_connectivity = "Mesh3_face_links"
2253+
Mesh3.edge_edge_connectivity = "Mesh3_edge_links"
2254+
2255+
# Node
2256+
Mesh3_node_x = n.createVariable("Mesh3_node_x", "f4", ("nMesh3_node",))
2257+
Mesh3_node_x.standard_name = "longitude"
2258+
Mesh3_node_x.units = "degrees_east"
2259+
Mesh3_node_x[...] = [-45, -43, -45, -43, -45, -43, -40]
2260+
2261+
Mesh3_node_y = n.createVariable("Mesh3_node_y", "f4", ("nMesh3_node",))
2262+
Mesh3_node_y.standard_name = "latitude"
2263+
Mesh3_node_y.units = "degrees_north"
2264+
Mesh3_node_y[...] = [35, 35, 33, 33, 31, 31, 34]
2265+
2266+
Mesh3_edge_nodes = n.createVariable(
2267+
"Mesh3_edge_nodes", "i4", ("nMesh3_edge", "connectivity2")
2268+
)
2269+
Mesh3_edge_nodes.long_name = "Maps every edge to its two nodes"
2270+
Mesh3_edge_nodes[...] = [
2271+
[1, 6],
2272+
[3, 6],
2273+
[3, 1],
2274+
[0, 1],
2275+
[2, 0],
2276+
[2, 3],
2277+
[2, 4],
2278+
[5, 4],
2279+
[3, 5],
2280+
]
2281+
2282+
# Face
2283+
Mesh3_face_x = n.createVariable(
2284+
"Mesh3_face_x", "f8", ("nMesh3_face",), fill_value=-99
2285+
)
2286+
Mesh3_face_x.standard_name = "longitude"
2287+
Mesh3_face_x.units = "degrees_east"
2288+
Mesh3_face_x[...] = [-44, -44, -42]
2289+
2290+
Mesh3_face_y = n.createVariable(
2291+
"Mesh3_face_y", "f8", ("nMesh3_face",), fill_value=-99
2292+
)
2293+
Mesh3_face_y.standard_name = "latitude"
2294+
Mesh3_face_y.units = "degrees_north"
2295+
Mesh3_face_y[...] = [34, 32, 34]
2296+
2297+
Mesh3_face_nodes = n.createVariable(
2298+
"Mesh3_face_nodes",
2299+
"i4",
2300+
("nMesh3_face", "connectivity4"),
2301+
fill_value=-99,
2302+
)
2303+
Mesh3_face_nodes.long_name = "Maps every face to its corner nodes"
2304+
Mesh3_face_nodes[...] = [[2, 3, 1, 0], [4, 5, 3, 2], [6, 1, 3, -99]]
2305+
2306+
Mesh3_face_links = n.createVariable(
2307+
"Mesh3_face_links",
2308+
"i4",
2309+
("nMesh3_face", "connectivity4"),
2310+
fill_value=-99,
2311+
)
2312+
Mesh3_face_links.long_name = "neighbour faces for faces"
2313+
Mesh3_face_links[...] = [
2314+
[1, 2, -99, -99],
2315+
[0, -99, -99, -99],
2316+
[0, -99, -99, -99],
2317+
]
2318+
2319+
# Edge
2320+
Mesh3_edge_x = n.createVariable(
2321+
"Mesh3_edge_x", "f8", ("nMesh3_edge",), fill_value=-99
2322+
)
2323+
Mesh3_edge_x.standard_name = "longitude"
2324+
Mesh3_edge_x.units = "degrees_east"
2325+
Mesh3_edge_x[...] = [-41.5, -41.5, -43, -44, -45, -44, -45, -44, -43]
2326+
2327+
Mesh3_edge_y = n.createVariable(
2328+
"Mesh3_edge_y", "f8", ("nMesh3_edge",), fill_value=-99
2329+
)
2330+
Mesh3_edge_y.standard_name = "latitude"
2331+
Mesh3_edge_y.units = "degrees_north"
2332+
Mesh3_edge_y[...] = [34.5, 33.5, 34, 35, 34, 33, 32, 31, 32]
2333+
2334+
Mesh3_edge_links = n.createVariable(
2335+
"Mesh3_edge_links",
2336+
"i4",
2337+
("nMesh3_edge", "connectivity5"),
2338+
fill_value=-99,
2339+
)
2340+
Mesh3_edge_links.long_name = "neighbour edges for edges"
2341+
Mesh3_edge_links[...] = [
2342+
[1, 2, 3, -99, -99],
2343+
[0, 2, 5, 8, -99],
2344+
[3, 0, 1, 5, 8],
2345+
[4, 2, 0, -99, -99],
2346+
[
2347+
3,
2348+
5,
2349+
6,
2350+
-99,
2351+
-99,
2352+
],
2353+
[4, 6, 2, 1, 8],
2354+
[
2355+
4,
2356+
5,
2357+
7,
2358+
-99,
2359+
-99,
2360+
],
2361+
[
2362+
6,
2363+
8,
2364+
-99,
2365+
-99,
2366+
-99,
2367+
],
2368+
[7, 5, 2, 1, -99],
2369+
]
2370+
2371+
n.close()
2372+
return filename
2373+
2374+
22312375
def _make_aggregation_value(filename):
22322376
"""Create an aggregation variable with 'unique_values'."""
22332377
n = netCDF4.Dataset(filename, "w")
@@ -2341,6 +2485,7 @@ def _make_aggregation_value(filename):
23412485

23422486
ugrid_1 = _make_ugrid_1("ugrid_1.nc")
23432487
ugrid_2 = _make_ugrid_2("ugrid_2.nc")
2488+
ugrid_3 = _make_ugrid_3("ugrid_3.nc")
23442489

23452490
aggregation_value = _make_aggregation_value("aggregation_value.nc")
23462491

0 commit comments

Comments
 (0)