ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_StationSampler.H
Go to the documentation of this file.
1 /**
2  * \file ERF_StationSampler.H
3  */
4 #ifndef ERF_STATIONSAMPLER_H_
5 #define ERF_STATIONSAMPLER_H_
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <string>
10 
11 #include <AMReX_BoxArray.H>
12 #include <AMReX_REAL.H>
13 #include <AMReX_Vector.H>
14 
15 // invert_latlon_offset, used to place stations given by latitude/longitude
16 #include "ERF_LatLonMap.H"
17 
18 //
19 // Station time-series output.
20 //
21 // A station is a named set of geographic points at which a user-chosen set of
22 // variables is written to an ASCII time series, for comparison against
23 // meteorological tower and surface-station observations. The inputs follow the
24 // same shape as erf.refinement_indicators:
25 //
26 // erf.station_names = Lake1 Forests
27 // erf.Lake1.field = rain_accum
28 // erf.Lake1.lat = 45.13
29 // erf.Lake1.long = -122.34
30 // erf.Forests.field = magvel local_helicity
31 // erf.Forests.lat = 45.20 45.41
32 // erf.Forests.long = -122.10 -122.02
33 // erf.Forests.height_agl = 10.0 80.0
34 //
35 // lat/long are paired positionally, so the example above is two locations, not
36 // four. A station may instead be placed with .x/.y in domain coordinates, which
37 // is the only option for a run that has no lat/lon arrays. The heights apply to
38 // every location of the station and are given one way or the other, never both:
39 // .height_agl is metres above the local terrain, .height_abs is metres in the
40 // model's own vertical coordinate, the same z as geometry.prob_lo/prob_hi.
41 //
42 // The variable names accepted are the 3D plotfile variables and the built-in 2D
43 // diagnostics, and the values come from the same fill routines
44 // (ERF::FillPlot3DVars / ERF::FillPlot2DVars), so a station column and the
45 // corresponding plotfile component cannot disagree. A 2D plotfile can also
46 // carry sampled-level fields, named for the field and the level such as
47 // 'theta_z100m'; those are not accepted here, since a station asks for the 3D
48 // variable and a height directly.
49 //
50 // Four more names are accepted, which neither catalog holds as a 3D variable:
51 // the earth-relative winds u_east, v_north, wind_speed and wind_dir. They are
52 // formed from the station's interpolated x_velocity and y_velocity, rotated by
53 // the grid orientation at the station (see ERF_EarthRelativeWind.H), so that a
54 // column compares directly with a tower on a rotated (e.g. Lambert) grid.
55 //
56 
57 // Which earth-relative wind a station variable is; None for every other variable
59 
60 inline StationEarthWind
61 station_earth_wind_from_name (const std::string& name)
62 {
63  if (name == "u_east") { return StationEarthWind::UEast; }
64  if (name == "v_north") { return StationEarthWind::VNorth; }
65  if (name == "wind_speed") { return StationEarthWind::WindSpeed; }
66  if (name == "wind_dir") { return StationEarthWind::WindDir; }
68 }
69 
70 struct StationVar
71 {
72  std::string name;
73  std::string units;
74  bool is_2d = false; // comes from the 2D diagnostic catalog
75  bool is_missing = false; // valid name this run does not produce (an inactive
76  // 2D diagnostic, or with
77  // erf.station_allow_unavailable a 3D variable)
78  bool cells_may_be_missing = false; // active 2D field whose cells may hold missing_value
80  int comp = -1; // component in the filled MultiFab
81  // An earth-relative wind is formed from two 3D components: comp holds
82  // x_velocity and comp_v y_velocity
84  int comp_v = -1;
85 };
86 
87 //
88 // Bilinear interpolation of the four values around a station, with weights
89 // (1-wx)(1-wy), wx(1-wy), (1-wx)wy and wx*wy. A value equal to missing_value, or
90 // not finite, is left out and the weights of the others are renormalised, so a
91 // missing neighbour cannot be blended in as a number; missing_value is returned
92 // only when every neighbour with a nonzero weight is missing.
93 //
94 inline amrex::Real
96  amrex::Real v01, amrex::Real v11,
97  amrex::Real wx, amrex::Real wy,
98  amrex::Real missing_value)
99 {
100  const amrex::Real v[4] = {v00, v10, v01, v11};
101  const amrex::Real w[4] = {(amrex::Real(1.0)-wx)*(amrex::Real(1.0)-wy),
102  wx*(amrex::Real(1.0)-wy),
103  (amrex::Real(1.0)-wx)*wy,
104  wx*wy};
105  amrex::Real sum = amrex::Real(0.0);
106  amrex::Real wsum = amrex::Real(0.0);
107  for (int n = 0; n < 4; ++n) {
108  if (w[n] > amrex::Real(0.0) && std::isfinite(v[n]) && v[n] != missing_value) {
109  sum += w[n] * v[n];
110  wsum += w[n];
111  }
112  }
113  return (wsum > amrex::Real(0.0)) ? sum / wsum : missing_value;
114 }
115 
117 {
118  bool use_latlon = false;
119 
120  // As requested in the inputs file
123 
124  // Domain coordinates of the station, resolved once at setup
127 
128  // lat/lon actually reached by (x,y); equals the request to within the
129  // accuracy of the inverse map, and is reported in the file header
132 
133  // Orientation of the grid x axis at the station, counterclockwise from true
134  // east, for the earth-relative winds. Without lat/lon arrays the grid is
135  // taken to be aligned with east.
138 
139  // Resolved against the current grids at every output step
140  int lev = 0;
141  int ic = 0; // cell containing (x,y)
142  int jc = 0;
143  int i0 = 0; // lower-left corner of the interpolation stencil
144  int j0 = 0;
145  int i1 = 0; // upper-right corner; equals i0 where the
146  int j1 = 0; // stencil collapses at a physical boundary
147  amrex::Real wx = amrex::Real(0.0); // weight of i1 relative to i0
149  int ktop = 0; // highest cell the vertical interpolation
150  // reads, which is as high as the chosen
151  // level is known to cover the stencil
152 
153  // Height of the chosen level's first cell centre above the zero the requested
154  // heights are measured from; a requested height below it is sampled there
156 
157  // Index into the owning station's column list of this location's first column
158  int col_begin = 0;
159 };
160 
161 //
162 // One scalar written every output step: one variable, at one location, at one
163 // height. Heights do not apply to 2D variables, whose height index is -1.
164 //
166 {
167  int station = 0;
168  int loc = 0;
169  int height = -1;
170  int var = 0;
171 };
172 
173 struct Station
174 {
175  std::string name;
176  amrex::Vector<StationVar> vars;
177  amrex::Vector<StationLoc> locs;
178  // The requested heights, in metres, and what they are measured from. The
179  // two input keys collapse to one list here: which one was given only decides
180  // where the column's zero is, and that is a single flag rather than two
181  // lists everything downstream would have to choose between.
182  amrex::Vector<amrex::Real> heights;
183  bool heights_are_agl = true; // .height_agl, else .height_abs
184 
185  // Global column indices owned by this station, in file column order
186  amrex::Vector<int> columns;
187 
188  bool has_3d_vars () const {
189  for (const auto& v : vars) { if (!v.is_2d) { return true; } }
190  return false;
191  }
192 };
193 
195 {
196 public:
197  // Parses erf.station_names and the per-station keys. Name validation needs
198  // the run's variable catalogs and is done by ERF::init_stations.
199  explicit StationSampler (const std::string& pp_prefix);
200 
201  [[nodiscard]] bool empty () const { return m_stations.empty(); }
202 
203  amrex::Vector<Station>& stations () { return m_stations; }
204  [[nodiscard]] const amrex::Vector<Station>& stations () const { return m_stations; }
205 
206  [[nodiscard]] int numColumns () const { return m_ncolumns; }
207 
208  // Assigns the global column index of every (location, height, variable)
209  // triple. Called by ERF::init_stations once the variables are resolved.
210  void buildColumns ();
211 
212  // Buffer one row of values. Writes through to disk once the buffer is full.
213  void appendRow (amrex::Real time, amrex::Real epoch_time, const amrex::Vector<amrex::Real>& values);
214 
215  // Write any buffered rows. Safe to call from any rank; only the IO rank writes.
216  void flush ();
217 
218  // A run that is not restarting starts its station files afresh; a restart
219  // appends to what the earlier run wrote.
220  void setRestart (bool is_restart) { m_is_restart = is_restart; }
221 
222  // On a restart, stop now if a file this run would append to describes a
223  // different set of columns. Called by ERF::init_stations once the columns
224  // are resolved, so that a setup error costs a setup rather than a run.
225  void checkRestartFiles () const;
226 
227  void setWriteTimestamp (bool write_timestamp, const std::string& datetime_format) {
228  m_write_timestamp = write_timestamp;
229  m_datetime_format = datetime_format;
230  }
231 
232  [[nodiscard]] int bufferSteps () const { return m_buffer_steps; }
233 
234  // erf.station_allow_unavailable: write a 3D variable this configuration
235  // cannot produce as a constant missing column instead of stopping the run
236  [[nodiscard]] bool allowUnavailable () const { return m_allow_unavailable; }
237 
238  // A height below the first cell centre is honored by clamping, which is
239  // worth saying once and not once per output step.
240  [[nodiscard]] bool lowHeightWarned () const { return m_low_height_warned; }
242 
243  // Which level each location was resolved to is reported once, since it is
244  // the thing about a station that is least obvious from the inputs file.
245  [[nodiscard]] bool levelsReported () const { return m_levels_reported; }
247 
248  // Resolving which level and which cells a station is read from costs a
249  // ParallelCopy, a stream synchronization and a broadcast, and the answer is a
250  // function of the grids and of the terrain under the station. With terrain
251  // that does not move, grids that have not changed give the answer already
252  // stored in the locations, so the work can be skipped: these remember which
253  // grids that was, and report whether it is still those.
254  [[nodiscard]] bool stencilsValidFor (const amrex::Vector<amrex::BoxArray>& ba,
255  int finest_level) const;
256  void rememberStencilGrids (const amrex::Vector<amrex::BoxArray>& ba, int finest_level);
257 
258 private:
259  void writeHeader (const Station& station, std::ostream& os) const;
260 
261  // The format tag and a hash of what the columns are: the variables, their
262  // units, the requested locations, the heights and their order. This is what
263  // a restart compares, rather than the header as a whole, so that rewording
264  // the header or resolving a coordinate to a slightly different value does
265  // not make an existing series un-restartable.
266  [[nodiscard]] std::string columnSignature (const Station& station) const;
267 
268  // Stops a restart that would append columns the file's header does not
269  // describe, which would otherwise pass unnoticed until the file was read.
270  void checkHeaderMatches (const Station& station, const std::string& filename) const;
271 
272  amrex::Vector<Station> m_stations;
273  int m_ncolumns = 0;
274  int m_buffer_steps = 100;
275  bool m_allow_unavailable = false;
276 
277  // m_rows[irow] holds the values of every column at m_times[irow]
278  amrex::Vector<amrex::Real> m_times;
279  amrex::Vector<amrex::Real> m_epoch_times;
280  amrex::Vector<amrex::Real> m_rows;
281 
282  // The grids the stencils currently stored in the locations were resolved
283  // against; empty until the first resolve.
284  amrex::Vector<amrex::BoxArray> m_stencil_grids;
285 
286  bool m_is_restart = false;
287  bool m_low_height_warned = false;
288  bool m_levels_reported = false;
289  bool m_write_timestamp = false;
290  std::string m_datetime_format;
291 
292  std::string m_dir = "Output_Stations";
293  bool m_opened = false;
294 };
295 
296 #endif
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:116
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:23
amrex::Real Real
Definition: ERF_ShocInterface.H:19
StationEarthWind station_earth_wind_from_name(const std::string &name)
Definition: ERF_StationSampler.H:61
StationEarthWind
Definition: ERF_StationSampler.H:58
amrex::Real station_bilinear_skip_missing(amrex::Real v00, amrex::Real v10, amrex::Real v01, amrex::Real v11, amrex::Real wx, amrex::Real wy, amrex::Real missing_value)
Definition: ERF_StationSampler.H:95
Definition: ERF_StationSampler.H:195
StationSampler(const std::string &pp_prefix)
Definition: ERF_StationSampler.cpp:223
bool m_write_timestamp
Definition: ERF_StationSampler.H:289
bool m_levels_reported
Definition: ERF_StationSampler.H:288
int m_ncolumns
Definition: ERF_StationSampler.H:273
bool m_is_restart
Definition: ERF_StationSampler.H:286
amrex::Vector< Station > m_stations
Definition: ERF_StationSampler.H:272
std::string columnSignature(const Station &station) const
Definition: ERF_StationSampler.cpp:406
void buildColumns()
Definition: ERF_StationSampler.cpp:359
amrex::Vector< Station > & stations()
Definition: ERF_StationSampler.H:203
void setRestart(bool is_restart)
Definition: ERF_StationSampler.H:220
amrex::Vector< amrex::BoxArray > m_stencil_grids
Definition: ERF_StationSampler.H:284
int m_buffer_steps
Definition: ERF_StationSampler.H:274
bool m_low_height_warned
Definition: ERF_StationSampler.H:287
amrex::Vector< amrex::Real > m_epoch_times
Definition: ERF_StationSampler.H:279
void writeHeader(const Station &station, std::ostream &os) const
Definition: ERF_StationSampler.cpp:444
amrex::Vector< amrex::Real > m_rows
Definition: ERF_StationSampler.H:280
void flush()
Definition: ERF_StationSampler.cpp:636
bool empty() const
Definition: ERF_StationSampler.H:201
const amrex::Vector< Station > & stations() const
Definition: ERF_StationSampler.H:204
void setLevelsReported()
Definition: ERF_StationSampler.H:246
void checkHeaderMatches(const Station &station, const std::string &filename) const
Definition: ERF_StationSampler.cpp:523
void checkRestartFiles() const
Definition: ERF_StationSampler.cpp:601
bool allowUnavailable() const
Definition: ERF_StationSampler.H:236
std::string m_datetime_format
Definition: ERF_StationSampler.H:290
void rememberStencilGrids(const amrex::Vector< amrex::BoxArray > &ba, int finest_level)
Definition: ERF_StationSampler.cpp:629
bool stencilsValidFor(const amrex::Vector< amrex::BoxArray > &ba, int finest_level) const
Definition: ERF_StationSampler.cpp:619
void appendRow(amrex::Real time, amrex::Real epoch_time, const amrex::Vector< amrex::Real > &values)
Definition: ERF_StationSampler.cpp:379
void setWriteTimestamp(bool write_timestamp, const std::string &datetime_format)
Definition: ERF_StationSampler.H:227
int numColumns() const
Definition: ERF_StationSampler.H:206
bool m_allow_unavailable
Definition: ERF_StationSampler.H:275
std::string m_dir
Definition: ERF_StationSampler.H:292
amrex::Vector< amrex::Real > m_times
Definition: ERF_StationSampler.H:278
void setLowHeightWarned()
Definition: ERF_StationSampler.H:241
int bufferSteps() const
Definition: ERF_StationSampler.H:232
bool lowHeightWarned() const
Definition: ERF_StationSampler.H:240
bool m_opened
Definition: ERF_StationSampler.H:293
bool levelsReported() const
Definition: ERF_StationSampler.H:245
Definition: ERF_StationSampler.H:166
int height
Definition: ERF_StationSampler.H:169
int station
Definition: ERF_StationSampler.H:167
int loc
Definition: ERF_StationSampler.H:168
int var
Definition: ERF_StationSampler.H:170
Definition: ERF_StationSampler.H:117
amrex::Real req_lat
Definition: ERF_StationSampler.H:121
amrex::Real wx
Definition: ERF_StationSampler.H:147
int j1
Definition: ERF_StationSampler.H:146
amrex::Real cos_alpha
Definition: ERF_StationSampler.H:136
amrex::Real wy
Definition: ERF_StationSampler.H:148
amrex::Real req_lon
Definition: ERF_StationSampler.H:122
int jc
Definition: ERF_StationSampler.H:142
int i0
Definition: ERF_StationSampler.H:143
int ktop
Definition: ERF_StationSampler.H:149
amrex::Real got_lat
Definition: ERF_StationSampler.H:130
amrex::Real sin_alpha
Definition: ERF_StationSampler.H:137
amrex::Real first_cc_height
Definition: ERF_StationSampler.H:155
int col_begin
Definition: ERF_StationSampler.H:158
amrex::Real x
Definition: ERF_StationSampler.H:125
int ic
Definition: ERF_StationSampler.H:141
int i1
Definition: ERF_StationSampler.H:145
bool use_latlon
Definition: ERF_StationSampler.H:118
amrex::Real y
Definition: ERF_StationSampler.H:126
amrex::Real got_lon
Definition: ERF_StationSampler.H:131
int j0
Definition: ERF_StationSampler.H:144
int lev
Definition: ERF_StationSampler.H:140
Definition: ERF_StationSampler.H:71
std::string units
Definition: ERF_StationSampler.H:73
amrex::Real missing_value
Definition: ERF_StationSampler.H:79
bool cells_may_be_missing
Definition: ERF_StationSampler.H:78
int comp
Definition: ERF_StationSampler.H:80
std::string name
Definition: ERF_StationSampler.H:72
int comp_v
Definition: ERF_StationSampler.H:84
bool is_missing
Definition: ERF_StationSampler.H:75
StationEarthWind earth_wind
Definition: ERF_StationSampler.H:83
bool is_2d
Definition: ERF_StationSampler.H:74
Definition: ERF_StationSampler.H:174
amrex::Vector< StationLoc > locs
Definition: ERF_StationSampler.H:177
bool has_3d_vars() const
Definition: ERF_StationSampler.H:188
bool heights_are_agl
Definition: ERF_StationSampler.H:183
std::string name
Definition: ERF_StationSampler.H:175
amrex::Vector< amrex::Real > heights
Definition: ERF_StationSampler.H:182
amrex::Vector< StationVar > vars
Definition: ERF_StationSampler.H:176
amrex::Vector< int > columns
Definition: ERF_StationSampler.H:186