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 //
16 // Station time-series output.
17 //
18 // A station is a named set of geographic points at which a user-chosen set of
19 // variables is written to an ASCII time series, for comparison against
20 // meteorological tower and surface-station observations. The inputs follow the
21 // same shape as erf.refinement_indicators:
22 //
23 // erf.station_names = Lake1 Forests
24 // erf.Lake1.field = rain_accum
25 // erf.Lake1.lat = 45.13
26 // erf.Lake1.long = -122.34
27 // erf.Forests.field = magvel local_helicity
28 // erf.Forests.lat = 45.20 45.41
29 // erf.Forests.long = -122.10 -122.02
30 // erf.Forests.height_agl = 10.0 80.0
31 //
32 // lat/long are paired positionally, so the example above is two locations, not
33 // four. A station may instead be placed with .x/.y in domain coordinates, which
34 // is the only option for a run that has no lat/lon arrays. The heights apply to
35 // every location of the station and are given one way or the other, never both:
36 // .height_agl is metres above the local terrain, .height_abs is metres in the
37 // model's own vertical coordinate, the same z as geometry.prob_lo/prob_hi.
38 //
39 // The variable names accepted are the 3D plotfile variables and the built-in 2D
40 // diagnostics, and the values come from the same fill routines
41 // (ERF::FillPlot3DVars / ERF::FillPlot2DVars), so a station column and the
42 // corresponding plotfile component cannot disagree. A 2D plotfile can also
43 // carry sampled-level fields, named for the field and the level such as
44 // 'theta_z100m'; those are not accepted here, since a station asks for the 3D
45 // variable and a height directly.
46 //
47 
48 //
49 // Invert the latitude/longitude map locally. Given the difference between the
50 // requested lat/lon and the lat/lon of a grid point, and the gradients of
51 // latitude and longitude with respect to the two horizontal index directions at
52 // that point, return the offset in cells from the grid point to the request.
53 //
54 // The map is smooth over a cell, so one linear solve places the station to well
55 // under a cell width, whatever the projection. Returns false when the two
56 // gradients are parallel and the map is therefore not locally invertible.
57 //
58 inline bool
60  amrex::Real dlat_di, amrex::Real dlon_di,
61  amrex::Real dlat_dj, amrex::Real dlon_dj,
63 {
64  const amrex::Real scale = std::max(std::max(std::abs(dlat_di), std::abs(dlon_di)),
65  std::max(std::abs(dlat_dj), std::abs(dlon_dj)));
66  const amrex::Real det = dlat_di*dlon_dj - dlat_dj*dlon_di;
67  if (scale <= amrex::Real(0.0) || std::abs(det) <= amrex::Real(1.0e-12)*scale*scale) {
68  return false;
69  }
70  di = ( dlat*dlon_dj - dlon*dlat_dj) / det;
71  dj = (-dlat*dlon_di + dlon*dlat_di) / det;
72  return true;
73 }
74 
75 struct StationVar
76 {
77  std::string name;
78  std::string units;
79  bool is_2d = false; // comes from the 2D diagnostic catalog
80  bool is_missing = false; // valid 2D name, inactive in this run
82  int comp = -1; // component in the filled MultiFab
83 };
84 
85 struct StationLoc
86 {
87  bool use_latlon = false;
88 
89  // As requested in the inputs file
92 
93  // Domain coordinates of the station, resolved once at setup
96 
97  // lat/lon actually reached by (x,y); equals the request to within the
98  // accuracy of the inverse map, and is reported in the file header
101 
102  // Resolved against the current grids at every output step
103  int lev = 0;
104  int ic = 0; // cell containing (x,y)
105  int jc = 0;
106  int i0 = 0; // lower-left corner of the interpolation stencil
107  int j0 = 0;
108  int i1 = 0; // upper-right corner; equals i0 where the
109  int j1 = 0; // stencil collapses at a physical boundary
110  amrex::Real wx = amrex::Real(0.0); // weight of i1 relative to i0
112  int ktop = 0; // highest cell the vertical interpolation
113  // reads, which is as high as the chosen
114  // level is known to cover the stencil
115 
116  // Index into the owning station's column list of this location's first column
117  int col_begin = 0;
118 };
119 
120 //
121 // One scalar written every output step: one variable, at one location, at one
122 // height. Heights do not apply to 2D variables, whose height index is -1.
123 //
125 {
126  int station = 0;
127  int loc = 0;
128  int height = -1;
129  int var = 0;
130 };
131 
132 struct Station
133 {
134  std::string name;
135  amrex::Vector<StationVar> vars;
136  amrex::Vector<StationLoc> locs;
137  // The requested heights, in metres, and what they are measured from. The
138  // two input keys collapse to one list here: which one was given only decides
139  // where the column's zero is, and that is a single flag rather than two
140  // lists everything downstream would have to choose between.
141  amrex::Vector<amrex::Real> heights;
142  bool heights_are_agl = true; // .height_agl, else .height_abs
143 
144  // Global column indices owned by this station, in file column order
145  amrex::Vector<int> columns;
146 
147  bool has_3d_vars () const {
148  for (const auto& v : vars) { if (!v.is_2d) { return true; } }
149  return false;
150  }
151 };
152 
154 {
155 public:
156  // Parses erf.station_names and the per-station keys. Name validation needs
157  // the run's variable catalogs and is done by ERF::init_stations.
158  explicit StationSampler (const std::string& pp_prefix);
159 
160  [[nodiscard]] bool empty () const { return m_stations.empty(); }
161 
162  amrex::Vector<Station>& stations () { return m_stations; }
163  [[nodiscard]] const amrex::Vector<Station>& stations () const { return m_stations; }
164 
165  [[nodiscard]] int numColumns () const { return m_ncolumns; }
166 
167  // Assigns the global column index of every (location, height, variable)
168  // triple. Called by ERF::init_stations once the variables are resolved.
169  void buildColumns ();
170 
171  // Buffer one row of values. Writes through to disk once the buffer is full.
172  void appendRow (amrex::Real time, amrex::Real epoch_time, const amrex::Vector<amrex::Real>& values);
173 
174  // Write any buffered rows. Safe to call from any rank; only the IO rank writes.
175  void flush ();
176 
177  // A run that is not restarting starts its station files afresh; a restart
178  // appends to what the earlier run wrote.
179  void setRestart (bool is_restart) { m_is_restart = is_restart; }
180 
181  // On a restart, stop now if a file this run would append to describes a
182  // different set of columns. Called by ERF::init_stations once the columns
183  // are resolved, so that a setup error costs a setup rather than a run.
184  void checkRestartFiles () const;
185 
186  void setWriteTimestamp (bool write_timestamp, const std::string& datetime_format) {
187  m_write_timestamp = write_timestamp;
188  m_datetime_format = datetime_format;
189  }
190 
191  [[nodiscard]] int bufferSteps () const { return m_buffer_steps; }
192 
193  // A height below the first cell centre is honored by clamping, which is
194  // worth saying once and not once per output step.
195  [[nodiscard]] bool lowHeightWarned () const { return m_low_height_warned; }
197 
198  // Which level each location was resolved to is reported once, since it is
199  // the thing about a station that is least obvious from the inputs file.
200  [[nodiscard]] bool levelsReported () const { return m_levels_reported; }
202 
203  // Resolving which level and which cells a station is read from costs a
204  // ParallelCopy, a stream synchronization and a broadcast, and the answer is a
205  // function of the grids and of the terrain under the station. With terrain
206  // that does not move, grids that have not changed give the answer already
207  // stored in the locations, so the work can be skipped: these remember which
208  // grids that was, and report whether it is still those.
209  [[nodiscard]] bool stencilsValidFor (const amrex::Vector<amrex::BoxArray>& ba,
210  int finest_level) const;
211  void rememberStencilGrids (const amrex::Vector<amrex::BoxArray>& ba, int finest_level);
212 
213 private:
214  void writeHeader (const Station& station, std::ostream& os) const;
215 
216  // The format tag and a hash of what the columns are: the variables, their
217  // units, the requested locations, the heights and their order. This is what
218  // a restart compares, rather than the header as a whole, so that rewording
219  // the header or resolving a coordinate to a slightly different value does
220  // not make an existing series un-restartable.
221  [[nodiscard]] std::string columnSignature (const Station& station) const;
222 
223  // Stops a restart that would append columns the file's header does not
224  // describe, which would otherwise pass unnoticed until the file was read.
225  void checkHeaderMatches (const Station& station, const std::string& filename) const;
226 
227  amrex::Vector<Station> m_stations;
228  int m_ncolumns = 0;
229  int m_buffer_steps = 100;
230 
231  // m_rows[irow] holds the values of every column at m_times[irow]
232  amrex::Vector<amrex::Real> m_times;
233  amrex::Vector<amrex::Real> m_epoch_times;
234  amrex::Vector<amrex::Real> m_rows;
235 
236  // The grids the stencils currently stored in the locations were resolved
237  // against; empty until the first resolve.
238  amrex::Vector<amrex::BoxArray> m_stencil_grids;
239 
240  bool m_is_restart = false;
241  bool m_low_height_warned = false;
242  bool m_levels_reported = false;
243  bool m_write_timestamp = false;
244  std::string m_datetime_format;
245 
246  std::string m_dir = "Output_Stations";
247  bool m_opened = false;
248 };
249 
250 #endif
amrex::Real Real
Definition: ERF_ShocInterface.H:19
bool invert_latlon_offset(amrex::Real dlat, amrex::Real dlon, amrex::Real dlat_di, amrex::Real dlon_di, amrex::Real dlat_dj, amrex::Real dlon_dj, amrex::Real &di, amrex::Real &dj)
Definition: ERF_StationSampler.H:59
Definition: ERF_StationSampler.H:154
StationSampler(const std::string &pp_prefix)
Definition: ERF_StationSampler.cpp:211
bool m_write_timestamp
Definition: ERF_StationSampler.H:243
bool m_levels_reported
Definition: ERF_StationSampler.H:242
int m_ncolumns
Definition: ERF_StationSampler.H:228
bool m_is_restart
Definition: ERF_StationSampler.H:240
amrex::Vector< Station > m_stations
Definition: ERF_StationSampler.H:227
std::string columnSignature(const Station &station) const
Definition: ERF_StationSampler.cpp:392
void buildColumns()
Definition: ERF_StationSampler.cpp:345
amrex::Vector< Station > & stations()
Definition: ERF_StationSampler.H:162
void setRestart(bool is_restart)
Definition: ERF_StationSampler.H:179
amrex::Vector< amrex::BoxArray > m_stencil_grids
Definition: ERF_StationSampler.H:238
int m_buffer_steps
Definition: ERF_StationSampler.H:229
bool m_low_height_warned
Definition: ERF_StationSampler.H:241
amrex::Vector< amrex::Real > m_epoch_times
Definition: ERF_StationSampler.H:233
void writeHeader(const Station &station, std::ostream &os) const
Definition: ERF_StationSampler.cpp:430
amrex::Vector< amrex::Real > m_rows
Definition: ERF_StationSampler.H:234
void flush()
Definition: ERF_StationSampler.cpp:605
bool empty() const
Definition: ERF_StationSampler.H:160
const amrex::Vector< Station > & stations() const
Definition: ERF_StationSampler.H:163
void setLevelsReported()
Definition: ERF_StationSampler.H:201
void checkHeaderMatches(const Station &station, const std::string &filename) const
Definition: ERF_StationSampler.cpp:492
void checkRestartFiles() const
Definition: ERF_StationSampler.cpp:570
std::string m_datetime_format
Definition: ERF_StationSampler.H:244
void rememberStencilGrids(const amrex::Vector< amrex::BoxArray > &ba, int finest_level)
Definition: ERF_StationSampler.cpp:598
bool stencilsValidFor(const amrex::Vector< amrex::BoxArray > &ba, int finest_level) const
Definition: ERF_StationSampler.cpp:588
void appendRow(amrex::Real time, amrex::Real epoch_time, const amrex::Vector< amrex::Real > &values)
Definition: ERF_StationSampler.cpp:365
void setWriteTimestamp(bool write_timestamp, const std::string &datetime_format)
Definition: ERF_StationSampler.H:186
int numColumns() const
Definition: ERF_StationSampler.H:165
std::string m_dir
Definition: ERF_StationSampler.H:246
amrex::Vector< amrex::Real > m_times
Definition: ERF_StationSampler.H:232
void setLowHeightWarned()
Definition: ERF_StationSampler.H:196
int bufferSteps() const
Definition: ERF_StationSampler.H:191
bool lowHeightWarned() const
Definition: ERF_StationSampler.H:195
bool m_opened
Definition: ERF_StationSampler.H:247
bool levelsReported() const
Definition: ERF_StationSampler.H:200
real(c_double), private di
Definition: ERF_module_mp_morr_two_moment.F90:203
Definition: ERF_StationSampler.H:125
int height
Definition: ERF_StationSampler.H:128
int station
Definition: ERF_StationSampler.H:126
int loc
Definition: ERF_StationSampler.H:127
int var
Definition: ERF_StationSampler.H:129
Definition: ERF_StationSampler.H:86
amrex::Real req_lat
Definition: ERF_StationSampler.H:90
amrex::Real wx
Definition: ERF_StationSampler.H:110
int j1
Definition: ERF_StationSampler.H:109
amrex::Real wy
Definition: ERF_StationSampler.H:111
amrex::Real req_lon
Definition: ERF_StationSampler.H:91
int jc
Definition: ERF_StationSampler.H:105
int i0
Definition: ERF_StationSampler.H:106
int ktop
Definition: ERF_StationSampler.H:112
amrex::Real got_lat
Definition: ERF_StationSampler.H:99
int col_begin
Definition: ERF_StationSampler.H:117
amrex::Real x
Definition: ERF_StationSampler.H:94
int ic
Definition: ERF_StationSampler.H:104
int i1
Definition: ERF_StationSampler.H:108
bool use_latlon
Definition: ERF_StationSampler.H:87
amrex::Real y
Definition: ERF_StationSampler.H:95
amrex::Real got_lon
Definition: ERF_StationSampler.H:100
int j0
Definition: ERF_StationSampler.H:107
int lev
Definition: ERF_StationSampler.H:103
Definition: ERF_StationSampler.H:76
std::string units
Definition: ERF_StationSampler.H:78
amrex::Real missing_value
Definition: ERF_StationSampler.H:81
int comp
Definition: ERF_StationSampler.H:82
std::string name
Definition: ERF_StationSampler.H:77
bool is_missing
Definition: ERF_StationSampler.H:80
bool is_2d
Definition: ERF_StationSampler.H:79
Definition: ERF_StationSampler.H:133
amrex::Vector< StationLoc > locs
Definition: ERF_StationSampler.H:136
bool has_3d_vars() const
Definition: ERF_StationSampler.H:147
bool heights_are_agl
Definition: ERF_StationSampler.H:142
std::string name
Definition: ERF_StationSampler.H:134
amrex::Vector< amrex::Real > heights
Definition: ERF_StationSampler.H:141
amrex::Vector< StationVar > vars
Definition: ERF_StationSampler.H:135
amrex::Vector< int > columns
Definition: ERF_StationSampler.H:145