REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_DataStruct.H
Go to the documentation of this file.
1#ifndef _REMORA_DATA_STRUCT_H_
2#define _REMORA_DATA_STRUCT_H_
3
4#include <string>
5#include <iostream>
6#include <array>
7
8#include <AMReX_ParmParse.H>
9#include <AMReX_Print.H>
10#include <AMReX_Gpu.H>
11
12#include <REMORA_Constants.H>
13#include <REMORA_DateClock.H>
14#include "REMORA_IndexDefines.H"
15
16/** \brief Type of coupling between levels in AMR */
17enum struct CouplingType {
19};
20
21/** \brief Coordinates */
22enum class Coord {
23 x, y, z
24};
25
26/** \brief Horizontal advection schemes */
30
31/** \brief Type of initial condition type. Analytic reads from prob.cpp. Netcdf is from file */
32enum class IC_Type {
34};
35
36/** \brief Coriolis factor */
37enum class Cor_Type {
39};
40
41/** \brief plotfile format */
42enum class PlotfileType {
44};
45
46/** \brief vertical mixing type */
47enum class VertMixingType {
49};
50
51/** \brief horizontal viscosity/diffusion type */
55
56/** \brief How to scale scaled_to_grid coefficients on AMR levels */
59};
60
61/** \brief stability function for GLS */
65
66/** \brief equation of state */
67enum class EOSType {
69};
70
71/** \brief bottom stress formulation */
75
76/** \brief initialization for pm and pn */
77enum class GridScaleType {
79};
80
81/** \brief surface momentum flux */
82enum class SMFluxType {
84};
85
86/** \brief surface wind */
87enum class WindType {
89};
90
91/** \brief masks */
92enum class MaskType {
94};
95
96/** \brief harmonic mixing; which surfaces to calculate along */
99};
100
102 public:
104 const std::string& param_name,
105 bool allow_computed)
106 {
107 const std::string type = amrex::toLower(type_string);
108 if (type == "constant" || type == "const") {
110 } else if (type == "custom") {
111 amrex::Warning((param_name + " uses 'custom'; use 'analytic' instead.").c_str());
113 } else if (type == "analytic" || type == "analytical") {
115 } else if (type == "netcdf" || type == "file" || type == "nc") {
117 } else if (type == "computed") {
118 if (!allow_computed) {
119 amrex::Abort((param_name + " does not accept 'computed'; only remora.lwrad_type and remora.eminusp_type do.").c_str());
120 }
122 }
123 amrex::Abort(("Don't know this " + param_name).c_str());
125 }
126
127 /** \brief read in and initialize parameters */
128 void init_params (int ncons, int nscalar, const amrex::Vector<std::string>& cons_names)
129 {
130 amrex::ParmParse pp(pp_prefix);
131
132 // Which horizontal advection scheme for tracers
133 static std::string tracer_hadv_string = "upstream3";
134 pp.queryAdd("tracer_horizontal_advection_scheme",tracer_hadv_string);
135 if (tracer_hadv_string == "centered4")
137 else if (tracer_hadv_string == "upstream3")
139 else
140 amrex::Error("Advection scheme unknown.");
141
142 // Which horizontal advection scheme
143 static std::string uv_hadv_string = "upstream3";
144 pp.queryAdd("uv_horizontal_advection_scheme",uv_hadv_string);
145 if (uv_hadv_string == "upstream3")
147 else if (uv_hadv_string == "centered2")
149 else
150 amrex::Error("UV advection scheme unknown.");
151
152 pp.queryAdd("rdrag", rdrag);
153 pp.queryAdd("rdrag2", rdrag2);
154 pp.queryAdd("Zos", Zos);
155 pp.queryAdd("Zob", Zob);
156 pp.queryAdd("Cdb_max", Cdb_max);
157 pp.queryAdd("Cdb_min", Cdb_min);
158
159 // Include salinity?
160 pp.queryAdd("use_salt", use_salt);
161
162 // Include Coriolis forcing?
163 pp.queryAdd("use_coriolis", use_coriolis);
164
165 // Include prestep / lagged predictor / corrections
166 pp.queryAdd("use_prestep", use_prestep);
167
168 //This affect forcing and some of the mixing terms for velocity
169 pp.queryAdd("use_uv3dmix", use_uv3dmix);
170
171 pp.queryAdd("use_curvilinear_grid", use_curvilinear_grid);
172
173 // Whether to do rivers. By default, rivers are temp and salt sources. Rivers
174 // have to be momentum sources.
175 pp.queryAdd("do_rivers", do_rivers);
176
177 pp.queryAdd("do_rivers_temp", do_rivers_temp);
178 pp.queryAdd("do_rivers_salt", do_rivers_salt);
179 pp.queryAdd("do_rivers_scalar", do_rivers_scalar);
180
181 // Every tracer may take river input, keyed by its own name, so temp and salt keep
182 // do_rivers_temp / do_rivers_salt and other tracers use e.g. do_rivers_NO3 or
183 // do_rivers_tracer_1. The passive scalars default to do_rivers_scalar; biology
184 // tracers default off, since a river concentration for them has to be a deliberate
185 // choice. If we aren't doing rivers at all, every flag ends up false.
186 do_rivers_cons.assign(ncons, 0);
187 for (int icomp = 0; icomp < ncons; ++icomp) {
188 bool flag = (icomp == Temp_comp) ? do_rivers_temp
190 : (icomp < Tracer_comp + nscalar) ? do_rivers_scalar
191 : false;
192 pp.queryAdd(("do_rivers_" + cons_names[icomp]).c_str(), flag);
193 do_rivers_cons[icomp] = (do_rivers && flag) ? 1 : 0;
194 }
195
196 pp.queryAdd("init_l1ad_T", init_l1ad_T);
197
198 pp.queryAdd("init_ana_T", init_ana_T);
199
200 pp.queryAdd("init_l0int_T", init_l0int_T);
201
202 static std::string eos_type_string = "linear";
203 pp.queryAdd("eos_type",eos_type_string);
204 if (eos_type_string == "linear" || eos_type_string == "Linear" ||
205 eos_type_string == "lin" || eos_type_string == "Lin") {
207 pp.queryAdd("Tcoef",Tcoef);
208 pp.queryAdd("Scoef",Scoef);
209 } else if (eos_type_string == "nonlinear" || eos_type_string == "Nonlinear" ||
210 eos_type_string == "non-linear" || eos_type_string == "Non-linear" ||
211 eos_type_string == "nonlin" || eos_type_string == "Nonlin") {
213 } else {
214 amrex::Abort("Dont know this eos_type");
215 }
216 pp.queryAdd("R0",R0);
217 pp.queryAdd("S0",S0);
218 pp.queryAdd("T0",T0);
219 pp.queryAdd("rho0", rho0);
220
221 // Reference date for the model clock, and with it the calendar, as ROMS
222 // TIME_REF: a yyyymmdd.dd date, or 0 for 0001-01-01, -1 for the 360-day
223 // calendar, -2 for truncated Julian days. See REMORA_DateClock.H.
224 pp.queryAdd("time_ref", time_ref);
226 // ROMS's own calendar dispatch has no final else, so a value below
227 // -2 leaves the date uninitialized rather than failing.
228 amrex::Abort("remora.time_ref = " + std::to_string(time_ref) +
229 " names no calendar. Use a yyyymmdd.dd date, or 0, -1, or -2.");
230 }
232 // Fail loudly rather than run on a silently shifted calendar: see
233 // remora_time_ref_is_representable.
234 amrex::Abort("remora.time_ref = " + std::to_string(time_ref) +
235 " is a yyyymmdd.dd date, which needs eight exact digits and so"
236 " cannot be represented in a single-precision build. Build in"
237 " double precision, or use time_ref = 0, -1, or -2.");
238 }
239
240 pp.queryAdd("bulk_fluxes",bulk_fluxes);
241 pp.queryAdd("atm2ocn_flux_mode", atm2ocn_flux_mode);
242 {
243 amrex::ParmParse pp_driver("driver");
244 std::string driver_atm2ocn_mode = "state";
245 pp_driver.query("atm2ocn_mode", driver_atm2ocn_mode);
246 if (amrex::toLower(driver_atm2ocn_mode) == "flux") {
247 atm2ocn_flux_mode = true;
248 }
249 }
251 do_salt_flux = true;
252 do_temp_flux = true;
253 }
254 // Outputs forcing variables if true
255 pp.queryAdd("output_forcing", output_forcing);
256 // The query return is the only moment "did the deck name this?" is
257 // knowable; see bulk_flux_value_specified. Legacy aliases (lwrad,
258 // eminusp_value) feed the same lane, so they count too.
259 auto value_specified = [&](int idx, int existed) {
260 if (existed) { bulk_flux_value_specified[idx] = true; }
261 };
262 value_specified(BulkFlux::Pair, pp.queryAdd("air_pressure",Pair));
263 value_specified(BulkFlux::Tair, pp.queryAdd("air_temperature",Tair));
264 value_specified(BulkFlux::Qair, pp.queryAdd("air_humidity",Hair));
265 value_specified(BulkFlux::SWrad, pp.queryAdd("surface_radiation_flux",srflux));
266 value_specified(BulkFlux::Uwind, pp.queryAdd("uwind", Uwind));
267 value_specified(BulkFlux::Vwind, pp.queryAdd("vwind", Vwind));
268 value_specified(BulkFlux::LWrad, pp.queryAdd("longwave_radiation_flux", longwave_rad));
270 value_specified(BulkFlux::LWrad, pp.queryAdd("longwave_down", longwave_down));
271 value_specified(BulkFlux::LWrad, pp.queryAdd("longwave_is_net", longwave_is_net));
272 pp.queryAdd("longwave_netcdf_varname", longwave_netcdf_varname);
273 value_specified(BulkFlux::Cloud, pp.queryAdd("cloud",cloud));
274 value_specified(BulkFlux::Rain, pp.queryAdd("rain",rain));
275 value_specified(BulkFlux::EminusP, pp.queryAdd("EminusP", EminusP));
276 value_specified(BulkFlux::EminusP, pp.query("eminusp_value", EminusP));
277 pp.queryAdd("blk_ZQ",blk_ZQ);
278 pp.queryAdd("blk_ZT",blk_ZT);
279 pp.queryAdd("blk_ZW",blk_ZW);
280 pp.queryAdd("eminusp",eminusp);
281 pp.queryAdd("eminusp_correct_ssh",eminusp_correct_ssh);
282 pp.queryAdd("qair_is_percent",qair_is_percent);
283
284 struct BulkTypeInput {
285 const char* name;
286 int idx;
287 const char* default_type;
288 bool allow_computed;
289 };
290
292 {"uwind_type", BulkFlux::Uwind, "analytic", false},
293 {"vwind_type", BulkFlux::Vwind, "analytic", false},
294 {"tair_type", BulkFlux::Tair, "constant", false},
295 {"qair_type", BulkFlux::Qair, "constant", false},
296 {"pair_type", BulkFlux::Pair, "constant", false},
297 {"swrad_type", BulkFlux::SWrad, "constant", false},
298 {"lwrad_type", BulkFlux::LWrad, "computed", true},
299 {"rain_type", BulkFlux::Rain, "constant", false},
300 {"cloud_type", BulkFlux::Cloud, "constant", false},
301 {"eminusp_type", BulkFlux::EminusP, "computed", true},
302 };
303
304 bool uwind_type_specified = false;
305 bool vwind_type_specified = false;
306 for (const BulkTypeInput& input : bulk_type_inputs) {
307 std::string type_string = input.default_type;
308 const bool type_specified = pp.queryAdd(input.name, type_string);
309 if (type_specified) {
311 "remora." + std::string(input.name),
312 input.allow_computed);
314 if (input.idx == BulkFlux::Uwind) {
316 } else if (input.idx == BulkFlux::Vwind) {
318 }
319 }
320 }
321
322 struct BulkTypeAlias {
323 const char* name;
324 int idx;
325 bool allow_computed;
326 };
327
329 {"srflx_type", BulkFlux::SWrad, false},
330 {"longwave_type", BulkFlux::LWrad, true},
331 {"longwave_down_type", BulkFlux::LWrad, true},
332 };
333
334 for (const BulkTypeAlias& input : bulk_type_aliases) {
335 std::string type_string;
336 if (pp.query(input.name, type_string)) {
338 "remora." + std::string(input.name),
339 input.allow_computed);
341 }
342 }
343
345 {"Tair_from_netcdf", BulkFlux::Tair, false},
346 {"qair_from_netcdf", BulkFlux::Qair, false},
347 {"Pair_from_netcdf", BulkFlux::Pair, false},
348 {"srflx_from_netcdf", BulkFlux::SWrad, false},
349 {"longwave_down_from_netcdf", BulkFlux::LWrad, false},
350 {"rain_from_netcdf", BulkFlux::Rain, false},
351 {"cloud_from_netcdf", BulkFlux::Cloud, false},
352 {"EminusP_from_netcdf", BulkFlux::EminusP, false},
353 };
354
356 bool from_netcdf = false;
357 if (pp.query(input.name, from_netcdf) && from_netcdf) {
360 }
361 }
362
364 if (pp.query("longwave_netcdf_is_net", legacy_longwave_netcdf_is_net) && legacy_longwave_netcdf_is_net) {
365 longwave_is_net = true;
369 }
370 }
371
374 }
375
377 amrex::Warning("remora.longwave_netcdf_varname is set but remora.lwrad_type is not netcdf; the value will be ignored.");
378 }
379
381 amrex::Abort("If evaporation minus precipitation (E-P) sea surface height correction is on, bulk fluxes must be on as well (remora.bulk_fluxes=true)");
382 }
383 if (eminusp and !bulk_fluxes) {
384 amrex::Abort("Evaporation minus precipitation (E-P) requires bulk flux parametrizations (remora.bulk_fluxes=true)");
385 }
386
387 const bool use_external_lwrad =
389
391 amrex::Abort("remora.longwave_is_net=true requires remora.lwrad_type to be constant, analytic, or netcdf");
392 }
393
395 amrex::Abort("remora.longwave_down=true requires remora.lwrad_type to be constant, analytic, or netcdf");
396 }
397
398 {
399 static bool printed_ep_source = false;
400 if (!printed_ep_source) {
401 printed_ep_source = true;
403 amrex::Print() << "[REMORA] Active E-P source: NetCDF EminusP (remora.eminusp_type=netcdf).\n";
405 amrex::Print() << "[REMORA] Active E-P source: analytic EminusP (remora.eminusp_type=analytic).\n";
407 amrex::Print() << "[REMORA] Active E-P source: constant EminusP (remora.eminusp_type=constant).\n";
408 } else {
409 amrex::Print() << "[REMORA] Active E-P source: bulk evap-rain diagnostic.\n";
410 }
411 }
412 }
413
414 //Grid stretching
415 pp.queryAdd("theta_s",theta_s);
416 pp.queryAdd("theta_b",theta_b);
417 pp.queryAdd("tcline",tcline);
418
419 //coriolis factor
420 pp.queryAdd("coriolis_f0",coriolis_f0);
421 pp.queryAdd("coriolis_beta",coriolis_beta);
422
423 pp.queryAdd("Akv_bak",Akv_bak);
424 // Minimum/initial vertical diffusivity for both active tracers. Override one of them
425 // with remora.Akt_bak_temp or remora.Akt_bak_salt; those keys are resolved below,
426 // once cons_names is in scope.
427 pp.queryAdd("Akt_bak",Akt_bak_all);
428
429
430 static std::string grid_scale_type_string = "constant";
431 pp.queryAdd("grid_scale_type",grid_scale_type_string);
432
433 if (amrex::toLower(grid_scale_type_string) == "constant") {
435 } else if (amrex::toLower(grid_scale_type_string) == "custom") {
436 amrex::Warning("Initialization of grid scale from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
438 } else if (amrex::toLower(grid_scale_type_string) == "analytic") {
440 } else {
441 amrex::Error("Don't know this grid_scale_type");
442 }
443
444 static std::string ic_type_string = "analytic";
445 bool found_ic_bc = pp.queryAdd("ic_bc_type", ic_type_string);
446 pp.queryAdd("ic_type", ic_type_string);
447
448 if (found_ic_bc) {
449 amrex::Warning("remora.ic_bc_type is now called remora.ic_type, and will eventually be deprecated");
450 }
451
452 if ( amrex::toLower(ic_type_string) == "custom") {
453 amrex::Warning("Problem initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
455 } else if ( amrex::toLower(ic_type_string) == "analytic") {
457 } else if ( amrex::toLower(ic_type_string) == "netcdf") {
459 } else if ( amrex::toLower(ic_type_string) == "real") {
460 amrex::Warning("Problem initialization from NetCDF (remora.ic_type) is now called 'netcdf'. 'real' will be deprecated");
462 } else {
463 amrex::Error("Don't know this ic_type");
464 }
465
466#ifndef REMORA_USE_NETCDF
467 // Without this the netcdf branches are compiled out one by one and the run integrates
468 // uninitialized state instead of failing: the #ifdef in REMORA::init_only sits inside
469 // the ic_type == netcdf branch rather than around it. mask_type below defaults to
470 // netcdf for a netcdf run, which would be silently skipped the same way.
471 if (ic_type == IC_Type::netcdf) {
472 amrex::Abort("remora.ic_type = netcdf requires a NetCDF build. Rebuild with "
473 "USE_PNETCDF=TRUE (GNUmake) or -DREMORA_ENABLE_PNETCDF=ON (CMake)");
474 }
475#endif
476
477 // Which type of refinement
478 static std::string coupling_type_string = "TwoWay";
479 pp.queryAdd("coupling_type",coupling_type_string);
480 if (amrex::toLower(coupling_type_string) == "twoway" ||
481 amrex::toLower(coupling_type_string) == "two_way") {
483 } else if (amrex::toLower(coupling_type_string) == "oneway" ||
484 amrex::toLower(coupling_type_string) == "one_way") {
486 } else {
487 amrex::Abort("Dont know this coupling_type");
488 }
489
490 // Which type of coriolis forcing
491 if (use_coriolis) {
492 static std::string coriolis_type_string = "beta_plane";
493 pp.queryAdd("coriolis_type",coriolis_type_string);
494 if ( amrex::toLower(coriolis_type_string) == "custom") {
495 amrex::Warning("Coriolis initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
497 } else if ( amrex::toLower(coriolis_type_string) == "analytic") {
499 } else if ((amrex::toLower(coriolis_type_string) == "beta_plane") ||
500 (amrex::toLower(coriolis_type_string) == "betaplane")) {
502 } else if ( (amrex::toLower(coriolis_type_string) == "netcdf")) {
504 } else if ( (amrex::toLower(coriolis_type_string) == "real")) {
505 amrex::Warning("Coriolis initialization from NetCDF is now called 'netcdf'. 'real' will be deprecated");
507 } else {
508 amrex::Abort("Don't know this coriolis_type");
509 }
510 }
511
512 static std::string smflux_type_string = "analytic";
513 int smflux_specified = pp.queryAdd("smflux_type",smflux_type_string);
514 if ( amrex::toLower(smflux_type_string) == "custom") {
515 amrex::Warning("Surface momentum flux initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
517 } else if ( amrex::toLower(smflux_type_string) == "analytic") {
519 } else if ( amrex::toLower(smflux_type_string) == "netcdf") {
521 } else {
522 amrex::Abort("Don't know this smflux_type");
523 }
524
525 static std::string wind_type_string = "analytic";
526 int wind_specified = pp.queryAdd("wind_type",wind_type_string);
527 if ( amrex::toLower(wind_type_string) == "custom") {
528 amrex::Warning("Surface wind initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
530 } else if ( amrex::toLower(wind_type_string) == "analytic") {
532 } else if ( amrex::toLower(wind_type_string) == "netcdf") {
534 } else {
535 amrex::Abort("Don't know this smflux_type");
536 }
537
538 if (wind_specified) {
541 // wind_type names both wind lanes, so it counts as specifying them
542 // even with the per-lane keys absent.
547 }
550 }
551 }
552
554 amrex::Abort("Cannot specify both wind and surface momentum flux");
555 }
556
557 static std::string mask_type_string = "none";
558 // If initial condition type is netcdf, default to netcdf masks
559 if (ic_type == IC_Type::netcdf) {
560 mask_type_string = "netcdf";
561 }
562 pp.queryAdd("mask_type", mask_type_string);
563 if (amrex::toLower(mask_type_string) == "none") {
565 } else if (amrex::toLower(mask_type_string) == "analytic") {
567 } else if (amrex::toLower(mask_type_string) == "netcdf") {
569 } else {
570 amrex::Abort("Don't know this mask_type");
571 }
572
573 static std::string bottom_stress_type_string = "linear";
574 pp.queryAdd("bottom_stress_type", bottom_stress_type_string);
575 if (amrex::toLower(bottom_stress_type_string) == "linear") {
577 } else if (amrex::toLower(bottom_stress_type_string) == "quadratic") {
579 } else if (amrex::toLower(bottom_stress_type_string) == "logarithmic") {
581 } else {
582 amrex::Abort("Don't know this bottom_stress_type");
583 }
584
585 amrex::Real tnu2_salt = zero;
586 amrex::Real tnu2_temp = zero;
587 amrex::Real tnu2_scalar = zero;
588 static std::string horiz_mixing_type_string = "analytic";
589 pp.queryAdd("horizontal_mixing_type", horiz_mixing_type_string);
590 if (amrex::toLower(horiz_mixing_type_string) == "analytical" ||
591 amrex::toLower(horiz_mixing_type_string) == "analytic") {
593 } else if (amrex::toLower(horiz_mixing_type_string) == "constant") {
595 } else if (amrex::toLower(horiz_mixing_type_string) == "scaled_to_grid") {
597 } else {
598 amrex::Abort("Don't know this horizontal mixing type");
599 }
600 pp.queryAdd("visc2",visc2);
601 pp.queryAdd("tnu2_salt",tnu2_salt);
602 pp.queryAdd("tnu2_temp",tnu2_temp);
603 pp.queryAdd("tnu2_scalar",tnu2_scalar);
604
605 // For scaled_to_grid runs with AMR refinement: optionally scale the coefficients
606 // by the horizontal refinement ratio (linear in grid size).
607 static std::string scaled_to_grid_amr_scaling_string = "none";
608 pp.queryAdd("scaled_to_grid_amr_scaling", scaled_to_grid_amr_scaling_string);
609 if (amrex::toLower(scaled_to_grid_amr_scaling_string) == "none") {
611 } else if (amrex::toLower(scaled_to_grid_amr_scaling_string) == "linear") {
613 } else {
614 amrex::Abort("Don't know this scaled_to_grid_amr_scaling option");
615 }
616
617 // Horizontal diffusivity is per tracer in ROMS, so mirror that here:
618 // remora.tnu2_scalar sets the group default and remora.tnu2_{var} overrides a single
619 // tracer by name -- remora.tnu2_NO3, remora.tnu2_tracer_1, and so on.
620 //
621 // tnu2_temp and tnu2_salt are the pre-existing spelling of that per-name form for the
622 // first two components. They keep their own defaults of zero rather than inheriting
623 // tnu2_scalar, so every existing input file means exactly what it did before, and the
624 // loop below skips them to avoid querying the same key twice.
625 tnu2.assign(ncons, tnu2_scalar);
626 if (ncons > Temp_comp) {
628 }
629 if (ncons > Salt_comp) {
631 }
632 for (int icomp = Tracer_comp; icomp < ncons; ++icomp) {
633 pp.queryAdd(("tnu2_" + cons_names[icomp]).c_str(), tnu2[icomp]);
634 }
635
636 // Vertical diffusivity is not. ROMS computes and stores Akt for the active tracers
637 // only, and every passive tracer -- dye and biology alike -- mixes with the salinity
638 // coefficient, so Akt_bak has NAT entries: remora.Akt_bak sets both and
639 // remora.Akt_bak_temp / remora.Akt_bak_salt override one.
640 Akt_bak.assign(NAT, Akt_bak_all);
641 for (int icomp = 0; icomp < NAT; ++icomp) {
642 pp.queryAdd(("Akt_bak_" + cons_names[icomp]).c_str(), Akt_bak[icomp]);
643 }
644 // A per-tracer key would name a coefficient nothing reads, and reading as though it
645 // had been applied is worse than not offering it, so say so rather than ignoring it.
646 for (int icomp = Tracer_comp; icomp < ncons; ++icomp) {
647 const std::string key = "Akt_bak_" + cons_names[icomp];
648 if (pp.contains(key.c_str())) {
649 amrex::Abort("remora." + key + " has no effect: vertical diffusivity is carried"
650 " for temperature and salinity only, and every passive tracer mixes"
651 " with the salinity value. Set remora.Akt_bak_salt instead, or"
652 " remora.tnu2_" + cons_names[icomp] + " if you meant the horizontal"
653 " diffusivity.");
654 }
655 }
656
657 static std::string harmonic_mixing_type_string = "s";
658 pp.queryAdd("harmonic_mixing_type", harmonic_mixing_type_string);
659 if (amrex::toLower(harmonic_mixing_type_string) == "s") {
661 } else if (amrex::toLower(harmonic_mixing_type_string) == "geopotential" ||
662 amrex::toLower(harmonic_mixing_type_string) == "geo") {
664 } else {
665 amrex::Abort("Don't know this harmonic_mixing_type");
666 }
667
668 pp.queryAdd("Akk_bak", Akk_bak);
669 pp.queryAdd("Akp_bak", Akp_bak);
670 static std::string vert_mixing_type_string = "analytic";
671 static std::string gls_stability_type_string = "Canuto_A";
672 pp.queryAdd("vertical_mixing_type", vert_mixing_type_string);
673 pp.queryAdd("gls_stability_type", gls_stability_type_string);
674 if (amrex::toLower(vert_mixing_type_string) == "analytical" ||
675 amrex::toLower(vert_mixing_type_string) == "analytic") {
677 } else if (amrex::toLower(vert_mixing_type_string) == "gls") {
679 if (amrex::toLower(gls_stability_type_string) == "canuto_a") {
681 }
682 else if (amrex::toLower(gls_stability_type_string) == "canuto_b") {
684 }
685 else if (amrex::toLower(gls_stability_type_string) == "galperin") {
687 }
688 else {
689 amrex::Abort("Don't know this GLS stability type");
690 }
691 } else {
692 amrex::Abort("Don't know this vertical mixing type");
693 }
694 // Read in GLS params
696 pp.queryAdd("gls_P", gls_p);
697 pp.queryAdd("gls_M", gls_m);
698 pp.queryAdd("gls_N", gls_n);
699 pp.queryAdd("gls_Kmin", gls_Kmin);
700 pp.queryAdd("gls_Pmin", gls_Pmin);
701
702 pp.queryAdd("gls_cmu0", gls_cmu0);
703 pp.queryAdd("gls_c1", gls_c1);
704 pp.queryAdd("gls_c2", gls_c2);
705 pp.queryAdd("gls_c3m", gls_c3m);
706 pp.queryAdd("gls_c3p", gls_c3p);
707 pp.queryAdd("gls_sigk", gls_sigk);
708 pp.queryAdd("gls_sigp", gls_sigp);
710 gls_Gh0 = amrex::Real(0.0329); // 0.0329 GOTM, 0.0673 Burchard
711 gls_Ghcri = amrex::Real(0.03);
712 gls_L1 = amrex::Real(0.107);
713 gls_L2 = amrex::Real(0.0032);
714 gls_L3 = amrex::Real(0.0864);
715 gls_L4 = amrex::Real(0.12);
716 gls_L5 = amrex::Real(11.9);
717 gls_L6 = amrex::Real(0.4);
718 gls_L7 = zero;
719 gls_L8 = amrex::Real(0.48);
721 gls_Gh0 = amrex::Real(0.0444); // 0.044 GOTM, 0.0673 Burchard
722 gls_Ghcri = amrex::Real(0.0414);
723 gls_L1 = amrex::Real(0.127);
724 gls_L2 = amrex::Real(0.00336);
725 gls_L3 = amrex::Real(0.0906);
726 gls_L4 = amrex::Real(0.101);
727 gls_L5 = amrex::Real(11.2);
728 gls_L6 = amrex::Real(0.4);
729 gls_L7 = zero;
730 gls_L8 = amrex::Real(0.318);
731 } else {
732 gls_Gh0 = amrex::Real(0.028);
733 gls_Ghcri = amrex::Real(0.02);
734 }
735 }
736
737 // Read and compute inverse nudging coeffs from inputs given in days,
738 // and store in a vector corresponding to BdyVars enum
739 amrex::Real tnudg = zero;
740 amrex::Real znudg = zero;
741 amrex::Real m2nudg = zero;
742 amrex::Real m3nudg = zero;
743 pp.queryAdd("tnudg",tnudg);
744 pp.queryAdd("znudg",znudg);
745 pp.queryAdd("m2nudg",m2nudg);
746 pp.queryAdd("m3nudg",m3nudg);
747 pp.queryAdd("obcfac",obcfac);
748
749 nudg_coeff.resize(BdyVars::NumTypes(ncons));
750 nudg_coeff[BdyVars::u ] = (m3nudg > zero) ? one / (m3nudg * amrex::Real(86400.0)) : zero;//BdyVars::u
751 nudg_coeff[BdyVars::v ] = (m3nudg > zero) ? one / (m3nudg * amrex::Real(86400.0)) : zero;//BdyVars::v
752 // Every cell-centered tracer -- temp, salt, and any additional passive or
753 // biology scalar -- nudges on the single tracer timescale tnudg, as in ROMS
754 // where Tnudg defaults to the same value for all tracers.
755 for (int icomp = 0; icomp < ncons; ++icomp) {
756 nudg_coeff[BdyVars::cons(icomp)] = (tnudg > zero) ? one / (tnudg * amrex::Real(86400.0)) : zero;
757 }
758 nudg_coeff[BdyVars::ubar(ncons)] = (m2nudg > zero) ? one / (m2nudg * amrex::Real(86400.0)) : zero;//BdyVars::ubar
759 nudg_coeff[BdyVars::vbar(ncons)] = (m2nudg > zero) ? one / (m2nudg * amrex::Real(86400.0)) : zero;//BdyVars::vbar
760 nudg_coeff[BdyVars::zeta(ncons)] = ( znudg > zero) ? one / ( znudg * amrex::Real(86400.0)) : zero;//BdyVars::zeta
761
762 pp.queryAdd("do_m3_clim_nudg", do_m3_clim_nudg);
763 pp.queryAdd("do_m2_clim_nudg", do_m2_clim_nudg);
764
765 // Every cell-centered tracer -- temp, salt, and any additional passive or biology
766 // scalar -- can be nudged toward its own climatology. The flag is keyed by the
767 // variable's own name, so temp and salt keep do_temp_clim_nudg / do_salt_clim_nudg
768 // and a biology tracer uses e.g. do_NO3_clim_nudg.
769 do_cons_clim_nudg.assign(ncons, 0);
770 for (int icomp = 0; icomp < ncons; ++icomp) {
771 bool do_nudg = false;
772 pp.queryAdd(("do_" + cons_names[icomp] + "_clim_nudg").c_str(), do_nudg);
774 if (do_nudg) { do_any_cons_clim_nudg = true; }
775 }
776
778 do_any_clim_nudg = true;
779 }
780 // The climatology series are only built along the NetCDF initialization path,
781 // so asking for nudging with analytic initial conditions would leave them
782 // unallocated. Say so here rather than failing later inside the time step.
784 amrex::Abort("Climatology nudging requires remora.ic_type = netcdf");
785 }
786#ifndef REMORA_USE_NETCDF
787 if (do_any_clim_nudg) {
788 amrex::Abort("Climatology nudging requires building with NetCDF");
789 }
790#endif
791 }
792
793 void display()
794 {
795 amrex::Print() << "SOLVER CHOICE: " << std::endl;
796 amrex::Print() << "use_salt : " << use_salt << std::endl;
797 amrex::Print() << "use_coriolis : " << use_coriolis << std::endl;
798 amrex::Print() << "use_prestep : " << use_prestep << std::endl;
799 amrex::Print() << "use_uv3dmix : " << use_uv3dmix << std::endl;
800 amrex::Print() << "spatial_order : " << spatial_order << std::endl;
801
802 if (ic_type == IC_Type::analytic) {
803 amrex::Print() << "Using analytic initial onditions" << std::endl;
804 }
805 else if (ic_type == IC_Type::netcdf) {
806 amrex::Print() << "Using NetCDF initial conditions" << std::endl;
807 }
808
810 amrex::Print() << "Horizontal advection scheme for tracers: " << "Centered 4" << std::endl;
811 }
813 amrex::Print() << "Horizontal advection scheme for tracers: " << "Upstream 3" << std::endl;
814 }
815 else {
816 amrex::Error("Invalid horizontal advection scheme for tracers.");
817 }
818
820 amrex::Print() << "Horizontal advection scheme for momenta: " << "Centered 2" << std::endl;
821 }
823 amrex::Print() << "Horizontal advection scheme for momenta: " << "Upstream 3" << std::endl;
824 }
825 else {
826 amrex::Error("Invalid horizontal advection scheme for momenta.");
827 }
828
830 amrex::Print() << "Using two-way coupling " << std::endl;
831 } else if (coupling_type == CouplingType::one_way) {
832 amrex::Print() << "Using one-way coupling " << std::endl;
833 }
834
835 if (use_coriolis) {
837 amrex::Print() << "Using analytic coriolis forcing " << std::endl;
838 } else if (coriolis_type == Cor_Type::beta_plane) {
839 amrex::Print() << "Using beta plane coriolis forcing " << std::endl;
840 } else if (coriolis_type == Cor_Type::netcdf) {
841 amrex::Print() << "Using coriolis forcing loaded from file " << std::endl;
842 }
843 }
844 }
845
846 // Default prefix
847 std::string pp_prefix {"remora"};
848
849 bool use_salt = true;
850
851 // Specify what additional physics/forcing modules we use
852 bool use_coriolis = false;
853
854 // Specify whether terms are used for debugging purposes
855 bool use_prestep = true;
856 bool use_uv3dmix = true;
857 bool use_baroclinic = true;
858
860
861 bool bulk_fluxes = false;
862 bool atm2ocn_flux_mode = false;
863
864 bool output_forcing = false;
865 bool do_temp_flux = false;
866 bool do_salt_flux = false;
867 bool longwave_down = false;
868
869 std::array<BulkForcingType, BulkFlux::NumTypes> bulk_flux_type {{
878 BulkForcingType::computed, // LWrad defaults to the internal longwave formula
879 BulkForcingType::computed // EminusP defaults to bulk evap-rain diagnostic
880 }};
881
882 // Which bulk-flux inputs the deck actually named, rather than defaulted to.
883 //
884 // Not recoverable afterwards: init_params reads these with queryAdd, which
885 // writes the default back into the table on a miss, so contains() then says
886 // "yes" for all of them. A coupling driver needs the distinction to decide
887 // whether to override a lane, so it is recorded here during parsing.
888 std::array<bool, BulkFlux::NumTypes> bulk_flux_type_specified {};
889 std::array<bool, BulkFlux::NumTypes> bulk_flux_value_specified {};
890
891 bool longwave_is_net = false;
892 bool qair_is_percent = false;
893 std::string longwave_netcdf_varname = "lwrad";
894
895
896 bool do_rivers = false;
897 bool do_rivers_temp = true;
898 bool do_rivers_salt = true;
899 bool do_rivers_scalar = false;
900 amrex::Vector<int> do_rivers_cons;
901
902 bool init_l1ad_T = false;
903
904 bool init_ana_T = false;
905
906 bool init_l0int_T = true;
907
910
911 // Coupling options are "OneWay" or "TwoWay"
913
914 // IC and BC Type: "analytic" or "netcdf"
916
917 // Coriolis forcing type
919
920 // Surface momentum flux type
922
923 // Surface wind speed type
925
926 // EOS type
928
929 // Bottom stress type
931
932 // Land/sea mask type
934
935 // Mixing type and parameters
941
942 // Type for grid scale (pm and pn)
944
945 // Stretching and depth parameters which may need to be read from inputs
946 amrex::Real theta_s = amrex::Real(3.0);
947 amrex::Real theta_b = zero;
948 amrex::Real tcline = amrex::Real(150.0);
949
950 // Linear drag coefficient [m/s]
951 amrex::Real rdrag = amrex::Real(3e-4);
952 // Quadratic drag coefficient [dimensionless]
953 amrex::Real rdrag2 = amrex::Real(3e-3);
954
955 // Momentum stress scales [m]
956 amrex::Real Zob = amrex::Real(2e-2);
957 amrex::Real Zos = amrex::Real(2e-2);
958
959 amrex::Real Cdb_max = amrex::Real(0.5);
960 amrex::Real Cdb_min = amrex::Real(1e-6);
961
962 // Linear equation of state parameters
963 amrex::Real R0 = amrex::Real(1028); // background density value (Kg/m3) used in Linear Equation of State
964 amrex::Real S0 = amrex::Real(35.0); // background salinity (nondimensional) constant
965 amrex::Real T0 = amrex::Real(5.0); // background potential temperature (Celsius) constant
966 amrex::Real Tcoef = amrex::Real(1.7e-4); // linear equation of state parameter (1/Celsius)
967 amrex::Real Scoef = zero; // linear equation of state parameter (nondimensional)
968 amrex::Real rho0 = amrex::Real(1025.0); // Mean density (Kg/m3) used when Boussinesq approx is inferred
969
970 // remora.time_ref: reference date of the model clock (yyyymmdd.dd), or one
971 // of the special values 0, -1, -2. Selects the calendar; see
972 // Source/Utils/REMORA_DateClock.H.
973 amrex::Real time_ref = amrex::Real(0.0);
974
975 // Coriolis forcing
976 amrex::Real coriolis_f0 = zero; // f-plane constant (1/s)
977 amrex::Real coriolis_beta = zero; // beta-plane constant (1/s/m)
978
979 // Air pressure
980 amrex::Real Pair = amrex::Real(1013.48);
981 // Air temperature
982 amrex::Real Tair = amrex::Real(23.567);
983 // Relative humidity (air)
984 amrex::Real Hair = amrex::Real(0.776);
985 // Cloud cover fraction (0=clear sky, 1=overcast)
986 amrex::Real cloud = zero;
987 // Precipitation rate (kg/m2/s)
988 amrex::Real rain = zero;
989 // Height (m) of atmospheric measurements for Bulk fluxes parametrization
990 amrex::Real blk_ZQ = amrex::Real(10.0); // air humidity
991 amrex::Real blk_ZT = amrex::Real(10.0); // air temperature
992 amrex::Real blk_ZW = amrex::Real(10.0); // winds
993
994 bool eminusp = false;
996
997 // Surface radiation flux
998 amrex::Real srflux = zero;
999 // Surface wind speed
1000 amrex::Real Uwind = zero;
1001 amrex::Real Vwind = zero;
1002 // External longwave radiation flux
1003 amrex::Real longwave_rad = zero;
1004 // Prescribed evaporation minus precipitation
1005 amrex::Real EminusP = zero;
1006
1007 // Spatial discretization
1009
1010 // Horizontal mixing parameters
1011 amrex::Real visc2 = zero;
1012 amrex::Vector<amrex::Real> tnu2;
1013
1014 // GLS params
1015 amrex::Real gls_p = amrex::Real(3.0);
1016 amrex::Real gls_m = amrex::Real(1.5);
1017 amrex::Real gls_n = amrex::Real(-1.0);
1018 amrex::Real gls_Kmin = amrex::Real(7.6e-6);
1019 amrex::Real gls_Pmin = amrex::Real(1.0e-12);
1020
1021 amrex::Real gls_cmu0 = amrex::Real(0.5477);
1022 amrex::Real gls_c1 = amrex::Real(1.44);
1023 amrex::Real gls_c2 = amrex::Real(1.92);
1024 amrex::Real gls_c3m = amrex::Real(-0.4);
1025 amrex::Real gls_c3p = one;
1026 amrex::Real gls_sigk = one;
1027 amrex::Real gls_sigp = amrex::Real(1.3);
1028
1029 // Turbulence closure
1030 amrex::Real Akk_bak = amrex::Real(5.0e-6);
1031 amrex::Real Akp_bak = amrex::Real(5.0e-6);
1032 amrex::Real Akv_bak = amrex::Real(5.0e-6);
1033 // remora.Akt_bak: the value both active tracers take unless overridden by name.
1034 amrex::Real Akt_bak_all = amrex::Real(1.0e-6);
1035 // NAT entries -- temperature and salinity -- to match the components of vec_Akt.
1036 // Passive tracers mix with entry Salt_comp; see akt_comp().
1037 amrex::Vector<amrex::Real> Akt_bak;
1038
1039 // Params for stability functions.
1040 amrex::Real gls_Gh0;
1041 amrex::Real gls_Ghcri;
1042 amrex::Real gls_Ghmin = amrex::Real(-0.28);
1043 amrex::Real gls_E2 = amrex::Real(1.33);
1044 // Params only for Canuto stability
1045 amrex::Real gls_L1;
1046 amrex::Real gls_L2;
1047 amrex::Real gls_L3;
1048 amrex::Real gls_L4;
1049 amrex::Real gls_L5;
1050 amrex::Real gls_L6;
1051 amrex::Real gls_L7;
1052 amrex::Real gls_L8;
1053
1054 // Params for some GLS and also Mellor-Yamada
1055 amrex::Real my_A1 = amrex::Real(0.92);
1056 amrex::Real my_A2 = amrex::Real(0.74);
1057 amrex::Real my_B1 = amrex::Real(16.6);
1058 amrex::Real my_B2 = amrex::Real(10.1);
1059 amrex::Real my_C1 = amrex::Real(0.08);
1060 amrex::Real my_C2 = amrex::Real(0.7);
1061 amrex::Real my_C3 = amrex::Real(0.2);
1062 amrex::Real my_E1 = amrex::Real(1.8);
1063 amrex::Real my_E2 = amrex::Real(1.33);
1064 amrex::Real my_Gh0 = amrex::Real(0.0233);
1065 amrex::Real my_Sq = amrex::Real(0.2);
1066 amrex::Real my_dtfac = amrex::Real(0.05);
1067 amrex::Real my_lmax = amrex::Real(0.53);
1068 amrex::Real my_qmin = amrex::Real(1.0E-8);
1069
1070 // Nudging time scales in 1/s
1071 amrex::Vector<amrex::Real> nudg_coeff;
1072
1073 // Factor between passive (outflow) and active (inflow) open boundary
1074 // conditions.
1075 amrex::Real obcfac = zero;
1076
1077 // Whether to do climatoogy nudging
1078 bool do_m2_clim_nudg = false;
1079 bool do_m3_clim_nudg = false;
1080 // Per cell-centered tracer, whether to nudge toward climatology. Indexed by
1081 // cons component, so entry Temp_comp is the old do_temp_clim_nudg.
1082 amrex::Vector<int> do_cons_clim_nudg;
1084 bool do_any_clim_nudg = false;
1085
1087};
1088
1089#endif
constexpr amrex::Real one
constexpr amrex::Real zero
BottomStressType
bottom stress formulation
GridScaleType
initialization for pm and pn
HarmonicMixingType
harmonic mixing; which surfaces to calculate along
SMFluxType
surface momentum flux
AdvectionScheme
Horizontal advection schemes.
PlotfileType
plotfile format
HorizMixingType
horizontal viscosity/diffusion type
Coord
Coordinates.
MaskType
masks
ScaledToGridAMRScaling
How to scale scaled_to_grid coefficients on AMR levels.
Cor_Type
Coriolis factor.
WindType
surface wind
GLS_StabilityType
stability function for GLS
CouplingType
Type of coupling between levels in AMR.
VertMixingType
vertical mixing type
IC_Type
Type of initial condition type. Analytic reads from prob.cpp. Netcdf is from file.
EOSType
equation of state
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool remora_time_ref_is_valid(amrex::Real time_ref) noexcept
Whether time_ref names a calendar this header implements.
AMREX_FORCE_INLINE bool remora_time_ref_is_representable(amrex::Real time_ref) noexcept
Whether time_ref survives a round trip through amrex::Real.
#define Temp_comp
#define NAT
BulkForcingType
Source type for bulk-flux atmospheric forcing variables.
#define Tracer_comp
#define Salt_comp
mf_h setVal(geomdata.ProbHi(2))
static constexpr int u
int NumTypes(int ncons) noexcept
static constexpr int v
int vbar(int ncons) noexcept
int cons(int icomp) noexcept
int zeta(int ncons) noexcept
int ubar(int ncons) noexcept
@ Vwind
10-m meridional wind [m/s]
@ Pair
atmospheric pressure [mb]
@ Uwind
10-m zonal wind [m/s]
@ LWrad
longwave radiation [W/m^2]
@ Tair
air temperature [degC]
@ Qair
specific humidity or relative humidity [kg/kg or fraction]
@ Cloud
cloud fraction [0-1]
@ SWrad
downward shortwave radiation [W/m^2]
@ Rain
precipitation rate [kg/m^2/s]
@ EminusP
evaporation minus precipitation [m/s]
amrex::Vector< amrex::Real > Akt_bak
GLS_StabilityType gls_stability_type
HorizMixingType horiz_mixing_type
amrex::Real Cdb_min
amrex::Real Akv_bak
amrex::Real blk_ZT
amrex::Real cloud
amrex::Real coriolis_beta
amrex::Real gls_sigp
amrex::Vector< amrex::Real > nudg_coeff
amrex::Real Akt_bak_all
amrex::Real rdrag2
std::array< bool, BulkFlux::NumTypes > bulk_flux_type_specified
amrex::Real my_lmax
amrex::Vector< amrex::Real > tnu2
amrex::Real gls_sigk
amrex::Real coriolis_f0
std::string longwave_netcdf_varname
AdvectionScheme uv_Hadv_scheme
amrex::Vector< int > do_rivers_cons
amrex::Real Tcoef
amrex::Real gls_cmu0
ScaledToGridAMRScaling scaled_to_grid_amr_scaling
std::string pp_prefix
AdvectionScheme tracer_Hadv_scheme
amrex::Real Akk_bak
amrex::Real blk_ZW
amrex::Real theta_b
amrex::Real theta_s
static BulkForcingType parse_bulk_forcing_type(const std::string &type_string, const std::string &param_name, bool allow_computed)
amrex::Real EminusP
amrex::Real tcline
amrex::Real gls_c3m
BottomStressType bottom_stress_type
amrex::Real gls_Gh0
amrex::Real gls_Ghmin
void init_params(int ncons, int nscalar, const amrex::Vector< std::string > &cons_names)
read in and initialize parameters
amrex::Real my_dtfac
amrex::Real longwave_rad
amrex::Real srflux
SMFluxType smflux_type
std::array< bool, BulkFlux::NumTypes > bulk_flux_value_specified
amrex::Real gls_Kmin
amrex::Real rdrag
VertMixingType vert_mixing_type
amrex::Real gls_c3p
std::array< BulkForcingType, BulkFlux::NumTypes > bulk_flux_type
amrex::Real blk_ZQ
amrex::Real Cdb_max
GridScaleType grid_scale_type
amrex::Real my_qmin
amrex::Real time_ref
amrex::Real gls_Ghcri
amrex::Real gls_Pmin
amrex::Vector< int > do_cons_clim_nudg
amrex::Real Scoef
HarmonicMixingType harmonic_mixing_type
amrex::Real Akp_bak
CouplingType coupling_type