YAC 3.13.0
Yet Another Coupler
Loading...
Searching...
No Matches
yac_record.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# Copyright (c) 2024 The YAC Authors
3#
4# SPDX-License-Identifier: BSD-3-Clause
5
6import numpy as np
7import uxarray as ux
8import yac
9import argparse
10import logging
11from isodate import parse_duration
12from mpi4py import MPI # < some MPI impl. need this to be imported explicitly
13
14parser = argparse.ArgumentParser("yac_replay",
15 description="""Replay simulation from a dataset.
16The given files are loaded with `uxarray.open_dataset`.
17It is currently not supported to run this component in parallel.""")
18parser.add_argument("gridfile", type=str,
19 default="path to the gridfile")
20parser.add_argument("datafile", type=str,
21 default="path to the dataset")
22parser.add_argument("variables", type=str, nargs="+",
23 help="Variable names to be recorded")
24parser.add_argument("--compname", type=str, default="record",
25 help="Name for the yac component (default: record)")
26parser.add_argument("--gridname", type=str, default="record_grid",
27 help="Name for the yac grid (default: record_grid)")
28parser.add_argument("--log-level", default=logging.WARNING, type=lambda x: getattr(logging, x.upper()),
29 help="Configure the logging level.")
30parser.add_argument("--coupling-config", type=str, default=None,
31 help="If given the yac config is read with yac.read_config_yaml")
32parser.add_argument("--points", type=str, nargs="*", choices=("cell", "edge", "vertex"),
33 default=[],
34 help="can be used to specify the entities on which the data should be recorded.")
35
36args = parser.parse_args()
37log_handler = logging.StreamHandler()
38log_handler.setFormatter(logging.Formatter("%(asctime)-10s %(name)-10s %(levelname)-8s %(message)s"))
39logging.basicConfig(level=args.log_level, handlers=[log_handler])
40
41logging.info("Instantiate yac")
43
44logging.info(f"reading config file {args.coupling_config!r}")
45
46if args.coupling_config:
47 y.read_config_yaml(args.coupling_config)
48
49logging.info(f"open dataset: {args.gridfile!r}, {args.datafile!r}")
50
51uxgrid = ux.open_grid(args.gridfile)
52
53logging.info(f"define component: {args.compname}")
54comp = y.def_comp(args.compname)
55
56fields = []
57
58
59cell_to_edge = np.asarray(uxgrid.face_edge_connectivity).reshape((-1,), order="C")
60cell_to_edge = cell_to_edge[cell_to_edge != ux.INT_FILL_VALUE]
61
62logging.info("defining grid")
63grid = yac.UnstructuredGridEdge(args.gridname,
64 uxgrid.n_nodes_per_face,
65 np.deg2rad(uxgrid.node_lon),
66 np.deg2rad(uxgrid.node_lat),
67 cell_to_edge, uxgrid.edge_node_connectivity)
68
69cell_point_id = grid.def_points(yac.Location.CELL,
70 np.deg2rad(uxgrid.face_lon),
71 np.deg2rad(uxgrid.face_lat))
72edge_point_id = grid.def_points(yac.Location.EDGE,
73 np.deg2rad(uxgrid.edge_lon),
74 np.deg2rad(uxgrid.edge_lat))
75vertex_point_id = grid.def_points(yac.Location.CORNER,
76 np.deg2rad(uxgrid.node_lon),
77 np.deg2rad(uxgrid.node_lat))
78
79if len(args.points) == 1:
80 args.points = len(args.variables)*[args.points[0]]
81args.points = args.points or len(args.variables)*["cell"]
82assert len(args.points) == len(args.variables), "If points are speified it must be given once or the same number as variables"
83
84logging.info("calling sync_def")
85
86y.sync_def()
87
88for varname in args.variables:
89 assert y.get_field_role(args.compname, args.gridname, varname) == yac.ExchangeType.TARGET, \
90 f"{varname!r} is not coupled to a target. Please define a coupling or remove it from the variables."
91
92start = np.datetime64(y.start_datetime)
93end = np.datetime64(y.end_datetime)
94
95
96first_source = y.get_field_source(args.compname, args.gridname, args.variables[0])
97dt = np.timedelta64(parse_duration(y.get_field_timestep(*first_source)))
98
99logging.info(f"Timestep is {dt}")
100
101# create the dataset
102ds = ux.UxDataset(uxgrid=uxgrid)
103ds["time"] = np.arange(start, end+dt, dt)
104
105for varname, points in zip(args.variables, args.points):
106 logging.info(f"configuring {varname!r}")
107 source = y.get_field_source(args.compname, args.gridname, varname)
108 logging.info(f"coupled to {source!r}")
109 assert dt == np.timedelta64(parse_duration(y.get_field_timestep(*source))), \
110 "Source fields of variables have incosistent timesteps"
111 collection_size = y.get_field_collection_size(*source)
112 logging.info(f"collection size: {collection_size}")
113 if collection_size > 1:
114 zaxis = [f"zaxis_{varname}"]
115 else:
116 zaxis = []
117 logging.info(f"zaxis: {zaxis}")
118 spatial_dim, spatial_size, point_id = \
119 {"cell": ("n_face", uxgrid.n_face, cell_point_id),
120 "edge": ("n_edge", uxgrid.n_edge, edge_point_id),
121 "vertex": ("n_node", uxgrid.n_node, vertex_point_id)}[points]
122 data = np.nan*np.ones((len(ds["time"]), *(len(zaxis)*[collection_size]), spatial_size))
123 ds = ds.assign({varname:
124 (("time", *zaxis, spatial_dim), data)})
125 fields.append((
126 yac.Field.create(varname,
127 comp,
128 point_id,
129 collection_size,
130 str(int(dt / np.timedelta64(1, 'ms'))), yac.TimeUnit.MILLISECOND),
131 ds[varname]
132 ))
133
134logging.info("calling enddef")
135y.enddef()
136
137while True:
138 for field, var in fields:
139 logging.info(f"processing {field.name} at {field.datetime}")
140 t = np.datetime64(field.datetime)
141 t_idx = np.searchsorted(ds["time"], t)
142 assert ds["time"][t_idx] == t, f"{t} not found in timeaxis"
143 data, info = field.get()
144 var.isel(time=t_idx).data[:] = data
145 logging.info(f"info: {info!r}")
146 if info == yac.Action.GET_FOR_RESTART:
147 break
148
149ds.to_netcdf(args.datafile)
create(cls, str field_name, Component comp, points, collection_size, str timestep, TimeUnit timeunit, masks=None)
Definition yac.pyx:1570
Initializies a YAC instance and provides further functionality.
Definition yac.pyx:693