YetAnotherCoupler 3.5.2
Loading...
Searching...
No Matches
grid_utils.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3# Copyright (c) 2024 The YAC Authors
4#
5# SPDX-License-Identifier: BSD-3-Clause
6
7from netCDF4 import Dataset
8import numpy as np
9import re
10import cartopy.crs as ccrs
11from pathlib import Path
12# import matplotlib.patheffects as PathEffects
13
14
15def to_numpy_deg(var):
16 if "rad" in var.units:
17 return np.rad2deg(var)
18 else:
19 return np.asarray(var) # assuming rad else
20
21
22def lonlat2xyz(lon, lat):
23 lon = np.deg2rad(lon)
24 lat = np.deg2rad(lat)
25 clat = np.cos(lat)
26 slat = np.sin(lat)
27 return np.stack([clat * np.cos(lon), clat * np.sin(lon), slat], axis=-1)
28
29
30def xyz2lonlat(xyz):
31 xyz = np.array(xyz)
32 lat = np.arcsin(xyz[..., 2])
33 lon = np.arctan2(xyz[..., 1], xyz[..., 0])
34 return np.rad2deg(lon), np.rad2deg(lat)
35
36
37def spherical_mean(lon, lat):
38 m = np.mean(lonlat2xyz(lon, lat), axis=1)
39 norm_inv = 1./np.linalg.norm(m, axis=-1)
40 return xyz2lonlat(norm_inv[:, None] * m)
41
42
44 """
45 Base class for Grids
46 """
47 def __init__(self):
48 self.transform = ccrs.Geodetic()
49
50 def plot(self, ax, label=None, plot_kwargs={},
51 key=None, cell_idx=None, edge_idx=None, corner_idx=None):
52 assert sum([cell_idx is not None, edge_idx is not None, corner_idx is not None]) <= 1
53 cell_sel = None
54 corner_sel = None
55 if cell_idx is not None:
56 edge_idx = np.where(np.isin(self.adjacent_cell_of_edge, cell_idx))
57 cell_sel = np.unique(cell_idx)
58 if corner_idx is not None:
59 edge_idx = np.where(np.isin(self.edge_vertices, corner_idx))
60 corner_sel = np.unique(corner_idx)
61 if edge_idx is None:
62 edge_idx = slice(None)
63 else:
64 edge_idx = np.unique(edge_idx)
65 edge_sel = edge_idx
66 if cell_sel is None:
67 cell_sel = slice(None) if edge_idx == slice(None) else \
68 self.adjacent_cell_of_edge[edge_sel]
69 if corner_sel is None:
70 corner_sel = slice(None) if edge_idx is None else \
71 np.unique(self.edge_vertices[:,edge_sel].flatten())
72
73 edges = self.edge_vertices[:, edge_idx]
74 x = self.vlon[edges]
75 y = self.vlat[edges]
76
77 # exclude edges outside the extent
78 # transform points to check the extent in the ax crs
79 p1 = ax.projection.transform_points(ccrs.PlateCarree(),
80 x[0, :], y[0, :])
81 p2 = ax.projection.transform_points(ccrs.PlateCarree(),
82 x[1, :], y[1, :])
83
84 extent = ax.get_extent()
85 mask = ((p1[:, 0] >= extent[0]) * (p1[:, 0] <= extent[1]) *
86 (p1[:, 1] >= extent[2]) * (p1[:, 1] <= extent[3])) + \
87 ((p2[:, 0] >= extent[0]) * (p2[:, 0] <= extent[1]) *
88 (p2[:, 1] >= extent[2]) * (p2[:, 1] <= extent[3]))
89
90 ax.plot(x[:, mask], y[:, mask], transform=self.transform,
91 **plot_kwargs)
92
93 text_kwargs = {}
94 if "color" in plot_kwargs:
95 text_kwargs["color"] = plot_kwargs["color"]
96
97 def add_label_in_extent(x, y, txt, label, pt_sel):
98 x = x[pt_sel]
99 y = y[pt_sel]
100 txt = txt[pt_sel]
101 p_t = ax.projection.transform_points(ccrs.PlateCarree(), x, y)
102 mask = ((p_t[:, 0] >= extent[0]) * (p_t[:, 0] <= extent[1]) *
103 (p_t[:, 1] >= extent[2]) * (p_t[:, 1] <= extent[3]))
104
105 if hasattr(self, 'slmsk'):
106 if label in self.slmsk:
107 mask = mask * self.slmsk[label][pt_sel]
108
109 return [ax.text(xx, yy, tt, ha="center", va="center", transform=ccrs.PlateCarree(),
110 #path_effects=[PathEffects.withStroke(linewidth=2, foreground='black')],
111 zorder=999,
112 **text_kwargs)
113 for xx, yy, tt in zip(x[mask], y[mask], txt[mask])]
114
115 def show_cell_labels(visibility):
116 if visibility and not hasattr(self, "cells_labels"):
117 self.cell_labels = add_label_in_extent(self.clon, self.clat,
118 np.arange(len(self.clon))+self.idx_offset,
119 'cell', cell_sel)
120 if hasattr(self, "cell_labels"):
121 for label in self.cell_labels:
122 label.set_visible(visibility)
123
124 def show_edge_labels(visibility):
125 if visibility and not hasattr(self, "edge_labels"):
126 self.edge_labels = add_label_in_extent(self.elon, self.elat,
127 np.arange(len(self.elon))+self.idx_offset,
128 'edge', edge_sel)
129 if hasattr(self, "edge_labels"):
130 for label in self.edge_labels:
131 label.set_visible(visibility)
132
133 def show_verts_labels(visibility):
134 if visibility and not hasattr(self, "verts_labels"):
135 self.verts_labels = add_label_in_extent(self.vlon, self.vlat,
136 np.arange(len(self.vlon))+self.idx_offset,
137 'vertex', corner_sel)
138 if hasattr(self, "verts_labels"):
139 for label in self.verts_labels:
140 label.set_visible(visibility)
141
142 self.label_state = 0
143 if label == "cell":
144 show_cell_labels(True)
145 self.label_state = 1
146 elif label == "edge":
147 show_edge_labels(True)
148 self.label_state = 2
149 elif label == "vertex":
150 show_verts_labels(True)
151 self.label_state = 3
152
153 def handle_key_press_event(event):
154 if event.key == key:
155 self.label_state = (self.label_state + 1) % 4
156 show_cell_labels(self.label_state == 1)
157 show_edge_labels(self.label_state == 2)
158 show_verts_labels(self.label_state == 3)
159 ax.figure.canvas.draw_idle()
160
161 if key:
162 ax.figure.canvas.mpl_connect('key_press_event', handle_key_press_event)
163
164 def save_netcdf(self, filename):
165 with Dataset(filename, "w") as dataset:
166 vdim = dataset.createDimension("vertex", self.vlon.shape[0])
167 vlon = dataset.createVariable("vlon", "f8", dimensions=(vdim,))
168 vlon[:] = np.deg2rad(self.vlon)
169 vlon.units = "radian"
170 vlon.long_name = "vertex longitude"
171 vlon.standard_name = "grid_longitude"
172 vlat = dataset.createVariable("vlat", "f8", dimensions=(vdim,))
173 vlat.units = "radian"
174 vlat.long_name = "vertex latitude"
175 vlat.standard_name = "grid_latitude"
176 vlat[:] = np.deg2rad(self.vlon)
177 cdim = dataset.createDimension("cell", self.vertex_of_cell.shape[1])
178 nv = dataset.createDimension("nv", self.vertex_of_cell.shape[0])
179 vertex_of_cell = dataset.createVariable("vertex_of_cell", "i", dimensions=(nv,cdim))
180 vertex_of_cell[:] = self.vertex_of_cell
181 vertex_of_cell.long_name = "vertices of each cell"
182 if hasattr(self, "clon") and hasattr(self, "clat"):
183 clon = dataset.createVariable("clon", "f8", dimensions=(cdim,))
184 clon.units = "radian"
185 clon.long_name = "center longitude"
186 clon.standard_name = "grid_longitude"
187 clon[:] = np.deg2rad(self.clon)
188 clat = dataset.createVariable("clat", "f8", dimensions=(cdim,))
189 clat.units = "radian"
190 clat.long_name = "center latitude"
191 clat.standard_name = "grid_latitude"
192 clat[:] = np.deg2rad(self.clon)
193 if hasattr(self, "elon") and hasattr(self, "elat"):
194 edim = dataset.createDimension("edge", self.elon.shape[0])
195 elon = dataset.createVariable("elon", "f8", dimensions=(edim,))
196 elon[:] = self.elon
197 elat = dataset.createVariable("elat", "f8", dimensions=(edim,))
198 elat[:] = self.elon
199
200 def def_yac(self, gridname, locations):
201 import yac
202 # currently only grids with same number of vertices per cell are supported
203 yac_grid = yac.UnstructuredGrid(gridname,
204 [self.vertex_of_cell.shape[0]]*self.vertex_of_cell.shape[1],
205 np.deg2rad(self.vlon), np.deg2rad(self.vlat),
206 self.vertex_of_cell.T.flatten())
207 yac_grid.set_global_index(self.idx_offset+np.arange(len(self.clon), dtype=int), yac.Location.CELL)
208 yac_grid.set_global_index(self.idx_offset+np.arange(len(self.vlon), dtype=int), yac.Location.CORNER)
209 coords = {yac.Location.CELL: (np.deg2rad(self.clon), np.deg2rad(self.clat)),
210 yac.Location.CORNER: (np.deg2rad(self.vlon), np.deg2rad(self.vlat))}
211 return [yac_grid.def_points(loc, *coords[loc]) for loc in locations]
212
213
215 """
216 Read grid from netCDF file like it is stored in ICON with fields
217 clon, clat, vlon, vlat, vertex_of_cell etc.
218 """
219 def __init__(self, string):
220 super().__init__()
221 filename = Path(string[5:]).expanduser() # truncate "icon:"
222 with Dataset(filename, "r") as dataset:
223 self.no_cells = dataset.dimensions["cell"].size
224 self.clonclon = to_numpy_deg(dataset["clon"])
225 self.clat = to_numpy_deg(dataset["clat"])
226 self.vlonvlon = to_numpy_deg(dataset["vlon"])
227 self.vlatvlat = to_numpy_deg(dataset["vlat"])
228 self.elonelon = to_numpy_deg(dataset["elon"])
229 self.elat = to_numpy_deg(dataset["elat"])
230 index_base = np.min(dataset["vertex_of_cell"])
231 self.vertex_of_cell = dataset["vertex_of_cell"] - index_base
232 self.edge_vertices = dataset["edge_vertices"] - index_base
233 self.adjacent_cell_of_edge = dataset["adjacent_cell_of_edge"] - index_base
234 self.idx_offsetidx_offset = index_base
235
236
238 """
239 Generate Gaussian grid with an given resolution and extent
240 (currently only on an closed interval in lon)
241 """
242 def __init__(self, string):
243 super().__init__()
244 m = re.match(r"([gG])([0-9]+),([0-9]+)(,(-?[0-9]+(\.[0-9]+)?),(-?[0-9]+(\.[0-9]+)?),(-?[0-9]+(\.[0-9]+)?),(-?[0-9]+(\.[0-9]+)?))?", string)
245 if not m:
246 raise Exception(f"Defintion of Gaussian grid {string} does not match regular expression.")
247 self.gridname = string
248 self.idx_offsetidx_offset = 0 if m.groups()[0] == "g" else 1
249 if m.groups()[3] is not None:
250 extent = [float(m.groups()[4]), float(m.groups()[6]), float(m.groups()[8]), float(m.groups()[10])]
251 else:
252 extent = [-180, 180, -90, 90]
253 res = [int(m.groups()[1]), int(m.groups()[2])]
254
255 self.lon = np.linspace(extent[0], extent[1], res[0]+1, endpoint=True)
256 self.lat = np.linspace(extent[2], extent[3], res[1]+1, endpoint=True)[::-1]
257 self.vlonvlon, self.vlatvlat = [x.flatten() for x in np.meshgrid(self.lon, self.lat)]
258 self.clonclon, self.clat = [x.flatten() for x in
259 np.meshgrid(self.lon[:-1] + 0.5*np.abs(self.lon[1]-self.lon[0]),
260 self.lat[1:] + 0.5*np.abs(self.lat[1]-self.lat[0]))]
261 self.no_cells = len(self.clat)
262 yidx, xidx = np.mgrid[0:res[1], 0:res[0]]
263
264 self.vertex_of_cell = np.stack([(xidx + yidx*(res[0]+1)).flatten(),
265 ((xidx+1) + yidx*(res[0]+1)).flatten(),
266 (xidx + (yidx+1)*(res[0]+1)).flatten(),
267 ((xidx+1) + (yidx+1)*(res[0]+1)).flatten()])
268
269 yidx, xidx = np.mgrid[0:res[1]+1, 0:res[0]]
270 hedges = np.stack([(xidx + yidx*(res[0]+1)).flatten(),
271 ((xidx+1) + yidx*(res[0]+1)).flatten()])
272 yidx, xidx = np.mgrid[0:res[1], 0:res[0]+1]
273 vedges = np.stack([(xidx + yidx *(res[0]+1)).flatten(),
274 (xidx + (yidx+1)*(res[0]+1)).flatten()])
275 self.edge_vertices = np.hstack([hedges, vedges])
276 self.elonelon = 0.5*np.sum(self.vlonvlon[self.edge_vertices], axis=0)
277 self.elat = 0.5*np.sum(self.vlatvlat[self.edge_vertices], axis=0)
278 self.transformtransform = ccrs.PlateCarree()
279
280 def def_yac(self, gridname, locations):
281 import yac
282 lon_rad = np.deg2rad(self.lon)
283 lat_rad = np.deg2rad(self.lat)
284 yac_grid = yac.Reg2dGrid(gridname, lon_rad, lat_rad)
285 coords = {yac.Location.CELL: (0.5*(lon_rad[1:]+lon_rad[:-1]), 0.5*(lat_rad[1:]+lat_rad[:-1])),
286 yac.Location.CORNER: (lon_rad, lat_rad)}
287 return [yac_grid.def_points(loc, *coords[loc]) for loc in locations]
288
289
291 """
292 HealPix Grid
293 """
294 def __init__(self, string):
295 super().__init__()
296 import healpy
297 m = re.match(r"([hH]([0-9]+))([rn])?", string)
298 if not m:
299 raise Exception(f"Defintion of HealPix grid {string} does not match regular expression.")
300 self.idx_offsetidx_offset = 0 if m.groups()[0] == "h" else 1
301 self.nside = int(m.groups()[1])
302 self.nest = m.groups()[2] != "r"
303 ncells = healpy.pixelfunc.nside2npix(self.nside)
304 centers_xyz = np.stack(
305 healpy.pixelfunc.pix2vec(self.nside, range(ncells), nest=self.nest),
306 axis=-1,
307 )
308 self.clonclon, self.clat = xyz2lonlat(centers_xyz)
309 boundaries_xyz = (
310 healpy.boundaries(self.nside, range(ncells), nest=self.nest)
311 .transpose(0, 2, 1)
312 .reshape(-1, 3)
313 )
314 verts_xyz, quads = np.unique(boundaries_xyz, return_inverse=True, axis=0)
315 self.vlonvlon, self.vlatvlat = xyz2lonlat(verts_xyz)
316 self.vertex_of_cell = quads.reshape(-1, 4).T
317 edges = np.hstack([self.vertex_of_cell[(i, (i+1) % 4), :] for i in range(4)])
318 edges = np.sort(edges, axis=0)
319 self.edge_vertices = np.unique(edges, axis=1)
320 self.elonelon, self.elat = spherical_mean(self.vlonvlon[self.edge_vertices].T, self.vlatvlat[self.edge_vertices].T)
321
322
324 def __init__(self, string):
325 super().__init__()
326 self.simple_dual = string[0] == "D"
327 prim_grid = get_grid(string[5:]) # truncate "dual:"
328 self.clonclon = prim_grid.vlon
329 self.clat = prim_grid.vlat
330 self.idx_offsetidx_offset = prim_grid.idx_offset
331 # compute corners
332 if hasattr(prim_grid, "clon") and hasattr(prim_grid, "clat"):
333 elem_centers_lon, elem_centers_lat = prim_grid.clon, prim_grid.clat
334 else:
335 elem_centers_lon, elem_centers_lat = spherical_mean(
336 prim_grid.vlon[prim_grid.vertex_of_cell], prim_grid.vlat[prim_grid.vertex_of_cell])
337
338 boundary_edges = (prim_grid.adjacent_cell_of_edge < 0)
339 is_boundary = sum(boundary_edges) != 0
340 boundary_nodes, bnodes_inverse = np.unique(
341 prim_grid.edge_vertices[:, boundary_edges[0, :] + boundary_edges[1, :]], return_inverse=True)
342 bnodes_inverse = bnodes_inverse.reshape(2, -1)
343
344 if self.simple_dual:
345 if hasattr(prim_grid, "elon") and hasattr(prim_grid, "elat"):
346 edge_centers_lon, edge_centers_lat = prim_grid.elon[is_boundary], prim_grid.elat[is_boundary]
347 else:
348 edge_centers_lon, edge_centers_lat = spherical_mean(
349 prim_grid.vlon[prim_grid.edge_vertices[:, is_boundary].T], prim_grid.vlat[prim_grid.edge_vertices[:, is_boundary].T])
350 else:
351 if hasattr(prim_grid, "elon") and hasattr(prim_grid, "elat"):
352 edge_centers_lon, edge_centers_lat = prim_grid.elon, prim_grid.elat
353 else:
354 edge_centers_lon, edge_centers_lat = spherical_mean(
355 prim_grid.vlon[prim_grid.edge_vertices.T], prim_grid.vlat[prim_grid.edge_vertices.T])
356
357 nelem = len(elem_centers_lon)
358 nedges = len(edge_centers_lon)
359 self.vlonvlon = np.concatenate([edge_centers_lon, elem_centers_lon, prim_grid.vlon[boundary_nodes]])
360 self.vlatvlat = np.concatenate([edge_centers_lat, elem_centers_lat, prim_grid.vlat[boundary_nodes]])
361
362 if self.simple_dual:
363 self.edge_vertices = np.hstack([
364 nedges + prim_grid.adjacent_cell_of_edge[:, ~is_boundary], # connect adjacent cell centers of non-boundary edges
365 nedges + nelem + bnodes_inverse,
366 np.stack([range(sum(is_boundary)), nedges + prim_grid.adjacent_cell_of_edge[boundary_edges[0, is_boundary].astype(int), is_boundary]])
367 ])
368 self.adjacent_cell_of_edge = np.hstack([
369 prim_grid.edge_vertices[:, ~is_boundary],
370 np.stack([boundary_nodes[bnodes_inverse[0, :]], -1*np.ones(bnodes_inverse[0, :].shape, dtype=int)]),
371 prim_grid.edge_vertices[:, is_boundary],
372 ])
373 else:
374 self.edge_vertices = np.hstack(
375 [np.stack([np.flatnonzero(~boundary_edges[0, :]), nedges+prim_grid.adjacent_cell_of_edge[0, ~boundary_edges[0, :]]]),
376 np.stack([np.flatnonzero(~boundary_edges[1, :]), nedges+prim_grid.adjacent_cell_of_edge[1, ~boundary_edges[1, :]]]),
377 np.stack([np.flatnonzero(is_boundary), nedges + nelem + bnodes_inverse[0, :]]),
378 np.stack([np.flatnonzero(is_boundary), nedges + nelem + bnodes_inverse[1, :]])
379 ])
380 self.adjacent_cell_of_edge = np.hstack([
381 prim_grid.edge_vertices[:, ~boundary_edges[0, :]],
382 prim_grid.edge_vertices[:, ~boundary_edges[1, :]],
383 np.stack([boundary_nodes[bnodes_inverse[0, :]], -1*np.ones(bnodes_inverse[0, :].shape, dtype=int)]),
384 np.stack([boundary_nodes[bnodes_inverse[1, :]], -1*np.ones(bnodes_inverse[1, :].shape, dtype=int)]),
385 ])
386
387 self.elonelon, self.elat = spherical_mean(self.vlonvlon[self.edge_vertices].T, self.vlatvlat[self.edge_vertices].T)
388
389
391 def __init__(self, string):
392 super().__init__()
393 self.idx_offsetidx_offset = 1 if string[0] == "F" else 0
394 path = Path(string[6:]).expanduser()
395 nodes = np.fromfile(path / "nod2d.out", sep=" ")[1:].reshape(-1, 4)
396 self.vertex_of_cell = np.fromfile(path / "elem2d.out", sep=" ", dtype=int)[1:].reshape(-1, 3).T - 1
397 self.edge_vertices = np.fromfile(path / "edges.out", sep=" ", dtype=int).reshape(-1, 2).T - 1
398 self.vlonvlon = (((180 + nodes[:, 1]) % 360) - 180)
399 self.vlatvlat = nodes[:, 2]
401 self.elonelon, self.elat = spherical_mean(self.vlonvlon[self.edge_vertices].T, self.vlatvlat[self.edge_vertices].T)
402 self.adjacent_cell_of_edge = np.fromfile(path / "edge_tri.out", sep=" ", dtype=int).reshape(-1, 2).T - 1
403
404
406 def __init__(self, string):
407 super().__init__()
408 parts = string.split(':')
409 path = string[6:].split(':')[0]
410 if len(parts) == 4:
411 _, path, mpath, grid = parts
412 has_masks = True
413 elif len(parts) == 3:
414 _, path, grid = parts
415 has_masks = False
416 else:
417 raise Exception(f"Invalid ScriptGrid definition: {string}")
418 self.gridname = grid
419 with Dataset(path, "r") as dataset:
420 x_size = dataset.dimensions["x_"+grid].size
421 y_size = dataset.dimensions["y_"+grid].size
422 self.no_cells = x_size * y_size
423 self.no_crn = dataset.dimensions["crn_"+grid].size
424 self.clonclon = (((180 + np.asfortranarray(dataset[grid+".lon"]).flatten()) % 360) - 180)
425 self.clat = np.asfortranarray(dataset[grid+".lat"]).flatten()
426 self.vlonvlon = (((180 + np.asfortranarray(dataset[grid+".clo"]).flatten()) % 360) - 180)
427 self.vlatvlat = np.asfortranarray(dataset[grid+".cla"]).flatten()
428 self.vertex_of_cell = np.arange(self.no_cells*self.no_crn).reshape(self.no_crn, -1)
429 if has_masks:
430 with Dataset(mpath, "r") as dataset:
431 self.slmskslmsk = {'cell': np.asfortranarray(dataset[grid+".msk"]).flatten()}
432 self.slmskslmsk['cell'] = self.slmskslmsk['cell'] == 0
433 self.vertex_of_cell = np.delete(self.vertex_of_cell,
434 np.where(~self.slmskslmsk['cell'])[0].tolist(), axis=1)
435 edges = np.hstack([self.vertex_of_cell[(i, (i+1) % self.no_crn), :] for i in range(self.no_crn)])
436 edges = np.sort(edges, axis=0)
437 self.edge_vertices, counts = np.unique(edges, axis=1, return_counts=True)
439 self.elonelon = 0.5*np.sum(self.vlonvlon[self.edge_vertices], axis=0)
440 self.elat = 0.5*np.sum(self.vlatvlat[self.edge_vertices], axis=0)
442
443
445 """
446 Read grid from netCDF file like it is stored by the YAC debug_grid feature
447 """
448 def __init__(self, string):
449 super().__init__()
450 prefix, filename, grid_name = string.split(":")
451 filename = Path(filename).expanduser() # truncate "yac_debug_grid:"
452 with Dataset(filename, "r") as dataset:
453 self.no_cells = dataset.dimensions["nc_"+grid_name].size
454 nv = dataset.dimensions["nv_"+grid_name].size
455 self.clonclon = to_numpy_deg(dataset[grid_name+".lon"]).T
456 self.clat = to_numpy_deg(dataset[grid_name+".lat"]).T
457 cla = to_numpy_deg(dataset[grid_name+".cla"])
458 clo = to_numpy_deg(dataset[grid_name+".clo"])
459 if grid_name+".vgid" in dataset.variables:
460 vgid = dataset[grid_name+".vgid"]
461 _, vidx, voc = np.unique(np.asarray(vgid).T, return_index=True, return_inverse=True)
462 self.vlonvlon = clo.T.flatten()[vidx]
463 self.vlatvlat = cla.T.flatten()[vidx]
464 self.vertex_of_cell = voc.reshape([nv, -1])
465 edges = np.stack([self.vertex_of_cell, self.vertex_of_cell[[*range(1, nv), 0], :]]).transpose(0, 2, 1).reshape(2, -1)
466 edges = np.sort(edges, axis=0)
467 self.edge_vertices, self.adjacent_cell_of_edge = np.unique(edges, return_inverse=True, axis=1)
468 self.elonelon, self.elat = spherical_mean(self.vlonvlon[self.edge_vertices].T, self.vlatvlat[self.edge_vertices].T)
469 else: # no chance to distinguish vertices:
470 self.vlonvlon = clo
471 self.vlatvlat = cla
472 self.vertex_of_cell = np.arange(self.no_cells*nv, dtype=int).reshape(-1, nv).T
473 self.edge_vertices = np.stack([self.vertex_of_cell, self.vertex_of_cell[[*range(1, nv), 0], :]]).transpose(0, 2, 1).reshape(2, -1)
474 self.elonelon, self.elat = spherical_mean(self.vlonvlon[self.edge_vertices].T, self.vlatvlat[self.edge_vertices].T)
475 self.adjacent_cell_of_edge = self.edge_vertices[0, :] // self.no_cells
477
478
479def get_grid(grid):
480 if grid.lower().startswith("icon:"):
481 return ICONGrid(grid)
482 if grid.lower().startswith("g"):
483 return GaussianGrid(grid)
484 if grid.lower().startswith("h"):
485 return HealPixGrid(grid)
486 if grid.lower().startswith("fesom:"):
487 return FesomGrid(grid)
488 if grid.lower().startswith("dual:"):
489 return DualGrid(grid)
490 if grid.lower().startswith("scrip:"):
491 return ScripGrid(grid)
492 if grid.lower().startswith("yac_debug_grid:"):
493 return YacDebugGrid(grid)
494 else:
495 raise Exception(f"cannot parse grid description: {grid}")
496
497
498if __name__ == "__main__":
499 import argparse
500 parser = argparse.ArgumentParser(prog="grid_utils.py")
501 subparsers = parser.add_subparsers(dest='command', help='sub-command help')
502 parser_save_grid = subparsers.add_parser('save_grid', help='save a grid to netCDF file')
503 parser_save_grid.add_argument('grid', type=str, help='grid')
504 parser_save_grid.add_argument('filename', type=Path, help='filename')
505 args = parser.parse_args()
506 if args.command == "save_grid":
507 get_grid(args.grid).save_netcdf(args.filename)
__init__(self, string)
__init__(self, string)
Generate Gaussian grid with an given resolution and extent (currently only on an closed interval in l...
def_yac(self, gridname, locations)
__init__(self, string)
Base class for Grids.
Definition grid_utils.py:43
save_netcdf(self, filename)
plot(self, ax, label=None, plot_kwargs={}, key=None, cell_idx=None, edge_idx=None, corner_idx=None)
Definition grid_utils.py:51
def_yac(self, gridname, locations)
__init__(self, string)
Read grid from netCDF file like it is stored in ICON with fields clon, clat, vlon,...
__init__(self, string)
__init__(self, string)
Read grid from netCDF file like it is stored by the YAC debug_grid feature.
__init__(self, string)
A stuctured 2d Grid.
Definition yac.pyx:1129
An unstuctured 2d Grid.
Definition yac.pyx:1245
get_grid(grid)
spherical_mean(lon, lat)
Definition grid_utils.py:37
to_numpy_deg(var)
Definition grid_utils.py:15
lonlat2xyz(lon, lat)
Definition grid_utils.py:22
xyz2lonlat(xyz)
Definition grid_utils.py:30