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 what to do when a level's mask disagrees with the level above it */
97enum class MaskConsistency {
98 abort, warn
99};
100
101/** \brief harmonic mixing; which surfaces to calculate along */
104};
105
107 public:
109 const std::string& param_name,
110 bool allow_computed)
111 {
112 const std::string type = amrex::toLower(type_string);
113 if (type == "constant" || type == "const") {
115 } else if (type == "custom") {
116 amrex::Warning((param_name + " uses 'custom'; use 'analytic' instead.").c_str());
118 } else if (type == "analytic" || type == "analytical") {
120 } else if (type == "netcdf" || type == "file" || type == "nc") {
122 } else if (type == "computed") {
123 if (!allow_computed) {
124 amrex::Abort((param_name + " does not accept 'computed'; only remora.lwrad_type and remora.eminusp_type do.").c_str());
125 }
127 }
128 amrex::Abort(("Don't know this " + param_name).c_str());
130 }
131
132 /** \brief read in and initialize parameters */
133 void init_params (int ncons, int nscalar, const amrex::Vector<std::string>& cons_names)
134 {
135 amrex::ParmParse pp(pp_prefix);
136
137 // Which horizontal advection scheme for tracers
138 static std::string tracer_hadv_string = "upstream3";
139 pp.queryAdd("tracer_horizontal_advection_scheme",tracer_hadv_string);
140 if (tracer_hadv_string == "centered4")
142 else if (tracer_hadv_string == "upstream3")
144 else
145 amrex::Error("Advection scheme unknown.");
146
147 // Which horizontal advection scheme
148 static std::string uv_hadv_string = "upstream3";
149 pp.queryAdd("uv_horizontal_advection_scheme",uv_hadv_string);
150 if (uv_hadv_string == "upstream3")
152 else if (uv_hadv_string == "centered2")
154 else
155 amrex::Error("UV advection scheme unknown.");
156
157 pp.queryAdd("rdrag", rdrag);
158 pp.queryAdd("rdrag2", rdrag2);
159 pp.queryAdd("Zos", Zos);
160 pp.queryAdd("Zob", Zob);
161 pp.queryAdd("Cdb_max", Cdb_max);
162 pp.queryAdd("Cdb_min", Cdb_min);
163
164 // Include salinity?
165 pp.queryAdd("use_salt", use_salt);
166
167 // Include Coriolis forcing?
168 pp.queryAdd("use_coriolis", use_coriolis);
169
170 // Include prestep / lagged predictor / corrections
171 pp.queryAdd("use_prestep", use_prestep);
172
173 //This affect forcing and some of the mixing terms for velocity
174 pp.queryAdd("use_uv3dmix", use_uv3dmix);
175
176 pp.queryAdd("use_curvilinear_grid", use_curvilinear_grid);
177
178 // Whether to do rivers. By default, rivers are temp and salt sources. Rivers
179 // have to be momentum sources.
180 pp.queryAdd("do_rivers", do_rivers);
181
182 pp.queryAdd("do_rivers_temp", do_rivers_temp);
183 pp.queryAdd("do_rivers_salt", do_rivers_salt);
184 pp.queryAdd("do_rivers_scalar", do_rivers_scalar);
185
186 // Every tracer may take river input, keyed by its own name, so temp and salt keep
187 // do_rivers_temp / do_rivers_salt and other tracers use e.g. do_rivers_NO3 or
188 // do_rivers_tracer_1. The passive scalars default to do_rivers_scalar; biology
189 // tracers default off, since a river concentration for them has to be a deliberate
190 // choice. If we aren't doing rivers at all, every flag ends up false.
191 do_rivers_cons.assign(ncons, 0);
192 for (int icomp = 0; icomp < ncons; ++icomp) {
193 bool flag = (icomp == Temp_comp) ? do_rivers_temp
195 : (icomp < Tracer_comp + nscalar) ? do_rivers_scalar
196 : false;
197 pp.queryAdd(("do_rivers_" + cons_names[icomp]).c_str(), flag);
198 do_rivers_cons[icomp] = (do_rivers && flag) ? 1 : 0;
199 }
200
201 pp.queryAdd("init_l1ad_T", init_l1ad_T);
202
203 pp.queryAdd("init_ana_T", init_ana_T);
204
205 pp.queryAdd("init_l0int_T", init_l0int_T);
206
207 static std::string eos_type_string = "linear";
208 pp.queryAdd("eos_type",eos_type_string);
209 if (eos_type_string == "linear" || eos_type_string == "Linear" ||
210 eos_type_string == "lin" || eos_type_string == "Lin") {
212 pp.queryAdd("Tcoef",Tcoef);
213 pp.queryAdd("Scoef",Scoef);
214 } else if (eos_type_string == "nonlinear" || eos_type_string == "Nonlinear" ||
215 eos_type_string == "non-linear" || eos_type_string == "Non-linear" ||
216 eos_type_string == "nonlin" || eos_type_string == "Nonlin") {
218 } else {
219 amrex::Abort("Dont know this eos_type");
220 }
221 pp.queryAdd("R0",R0);
222 pp.queryAdd("S0",S0);
223 pp.queryAdd("T0",T0);
224 pp.queryAdd("rho0", rho0);
225
226 // ROMS uses 9.81 exactly. Overridable so a run can be matched to ROMS to roundoff;
227 // the default is the physical value and every gold file assumes it.
228 pp.queryAdd("g", g);
229
230 // Reference date for the model clock, and with it the calendar, as ROMS
231 // TIME_REF: a yyyymmdd.dd date, or 0 for 0001-01-01, -1 for the 360-day
232 // calendar, -2 for truncated Julian days. See REMORA_DateClock.H.
233 pp.queryAdd("time_ref", time_ref);
235 // ROMS's own calendar dispatch has no final else, so a value below
236 // -2 leaves the date uninitialized rather than failing.
237 amrex::Abort("remora.time_ref = " + std::to_string(time_ref) +
238 " names no calendar. Use a yyyymmdd.dd date, or 0, -1, or -2.");
239 }
240
241 pp.queryAdd("bulk_fluxes",bulk_fluxes);
242 pp.queryAdd("atm2ocn_flux_mode", atm2ocn_flux_mode);
243 {
244 amrex::ParmParse pp_driver("driver");
245 std::string driver_atm2ocn_mode = "state";
246 pp_driver.query("atm2ocn_mode", driver_atm2ocn_mode);
247 if (amrex::toLower(driver_atm2ocn_mode) == "flux") {
248 atm2ocn_flux_mode = true;
249 }
250 }
252 do_salt_flux = true;
253 do_temp_flux = true;
254 }
255 // Outputs forcing variables if true
256 pp.queryAdd("output_forcing", output_forcing);
257 // The query return is the only moment "did the deck name this?" is
258 // knowable; see bulk_flux_value_specified. Legacy aliases (lwrad,
259 // eminusp_value) feed the same lane, so they count too.
260 auto value_specified = [&](int idx, int existed) {
261 if (existed) { bulk_flux_value_specified[idx] = true; }
262 };
263 value_specified(BulkFlux::Pair, pp.queryAdd("air_pressure",Pair));
264 value_specified(BulkFlux::Tair, pp.queryAdd("air_temperature",Tair));
265 value_specified(BulkFlux::Qair, pp.queryAdd("air_humidity",Hair));
266 value_specified(BulkFlux::SWrad, pp.queryAdd("surface_radiation_flux",srflux));
267 value_specified(BulkFlux::Uwind, pp.queryAdd("uwind", Uwind));
268 value_specified(BulkFlux::Vwind, pp.queryAdd("vwind", Vwind));
269 value_specified(BulkFlux::LWrad, pp.queryAdd("longwave_radiation_flux", longwave_rad));
271 value_specified(BulkFlux::LWrad, pp.queryAdd("longwave_down", longwave_down));
272 value_specified(BulkFlux::LWrad, pp.queryAdd("longwave_is_net", longwave_is_net));
273 pp.queryAdd("longwave_netcdf_varname", longwave_netcdf_varname);
274 value_specified(BulkFlux::Cloud, pp.queryAdd("cloud",cloud));
275 value_specified(BulkFlux::Rain, pp.queryAdd("rain",rain));
276 value_specified(BulkFlux::EminusP, pp.queryAdd("EminusP", EminusP));
277 value_specified(BulkFlux::EminusP, pp.query("eminusp_value", EminusP));
278 pp.queryAdd("blk_ZQ",blk_ZQ);
279 pp.queryAdd("blk_ZT",blk_ZT);
280 pp.queryAdd("blk_ZW",blk_ZW);
281 pp.queryAdd("eminusp",eminusp);
282 pp.queryAdd("eminusp_correct_ssh",eminusp_correct_ssh);
283 pp.queryAdd("qair_is_percent",qair_is_percent);
284
285 struct BulkTypeInput {
286 const char* name;
287 int idx;
288 const char* default_type;
289 bool allow_computed;
290 };
291
293 {"uwind_type", BulkFlux::Uwind, "analytic", false},
294 {"vwind_type", BulkFlux::Vwind, "analytic", false},
295 {"tair_type", BulkFlux::Tair, "constant", false},
296 {"qair_type", BulkFlux::Qair, "constant", false},
297 {"pair_type", BulkFlux::Pair, "constant", false},
298 {"swrad_type", BulkFlux::SWrad, "constant", false},
299 {"lwrad_type", BulkFlux::LWrad, "computed", true},
300 {"rain_type", BulkFlux::Rain, "constant", false},
301 {"cloud_type", BulkFlux::Cloud, "constant", false},
302 {"eminusp_type", BulkFlux::EminusP, "computed", true},
303 };
304
305 bool uwind_type_specified = false;
306 bool vwind_type_specified = false;
307 for (const BulkTypeInput& input : bulk_type_inputs) {
308 std::string type_string = input.default_type;
309 const bool type_specified = pp.queryAdd(input.name, type_string);
310 if (type_specified) {
312 "remora." + std::string(input.name),
313 input.allow_computed);
315 if (input.idx == BulkFlux::Uwind) {
317 } else if (input.idx == BulkFlux::Vwind) {
319 }
320 }
321 }
322
323 struct BulkTypeAlias {
324 const char* name;
325 int idx;
326 bool allow_computed;
327 };
328
330 {"srflx_type", BulkFlux::SWrad, false},
331 {"longwave_type", BulkFlux::LWrad, true},
332 {"longwave_down_type", BulkFlux::LWrad, true},
333 };
334
335 for (const BulkTypeAlias& input : bulk_type_aliases) {
336 std::string type_string;
337 if (pp.query(input.name, type_string)) {
339 "remora." + std::string(input.name),
340 input.allow_computed);
342 }
343 }
344
346 {"Tair_from_netcdf", BulkFlux::Tair, false},
347 {"qair_from_netcdf", BulkFlux::Qair, false},
348 {"Pair_from_netcdf", BulkFlux::Pair, false},
349 {"srflx_from_netcdf", BulkFlux::SWrad, false},
350 {"longwave_down_from_netcdf", BulkFlux::LWrad, false},
351 {"rain_from_netcdf", BulkFlux::Rain, false},
352 {"cloud_from_netcdf", BulkFlux::Cloud, false},
353 {"EminusP_from_netcdf", BulkFlux::EminusP, false},
354 };
355
357 bool from_netcdf = false;
358 if (pp.query(input.name, from_netcdf) && from_netcdf) {
361 }
362 }
363
365 if (pp.query("longwave_netcdf_is_net", legacy_longwave_netcdf_is_net) && legacy_longwave_netcdf_is_net) {
366 longwave_is_net = true;
370 }
371 }
372
375 }
376
378 amrex::Warning("remora.longwave_netcdf_varname is set but remora.lwrad_type is not netcdf; the value will be ignored.");
379 }
380
382 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)");
383 }
384 if (eminusp and !bulk_fluxes) {
385 amrex::Abort("Evaporation minus precipitation (E-P) requires bulk flux parametrizations (remora.bulk_fluxes=true)");
386 }
387
388 const bool use_external_lwrad =
390
392 amrex::Abort("remora.longwave_is_net=true requires remora.lwrad_type to be constant, analytic, or netcdf");
393 }
394
396 amrex::Abort("remora.longwave_down=true requires remora.lwrad_type to be constant, analytic, or netcdf");
397 }
398
399 {
400 static bool printed_ep_source = false;
401 if (!printed_ep_source) {
402 printed_ep_source = true;
404 amrex::Print() << "[REMORA] Active E-P source: NetCDF EminusP (remora.eminusp_type=netcdf).\n";
406 amrex::Print() << "[REMORA] Active E-P source: analytic EminusP (remora.eminusp_type=analytic).\n";
408 amrex::Print() << "[REMORA] Active E-P source: constant EminusP (remora.eminusp_type=constant).\n";
409 } else {
410 amrex::Print() << "[REMORA] Active E-P source: bulk evap-rain diagnostic.\n";
411 }
412 }
413 }
414
415 //Grid stretching
416 pp.queryAdd("theta_s",theta_s);
417 pp.queryAdd("theta_b",theta_b);
418 pp.queryAdd("tcline",tcline);
419
420 //coriolis factor
421 pp.queryAdd("coriolis_f0",coriolis_f0);
422 pp.queryAdd("coriolis_beta",coriolis_beta);
423
424 pp.queryAdd("Akv_bak",Akv_bak);
425 // Minimum/initial vertical diffusivity for both active tracers. Override one of them
426 // with remora.Akt_bak_temp or remora.Akt_bak_salt; those keys are resolved below,
427 // once cons_names is in scope.
428 pp.queryAdd("Akt_bak",Akt_bak_all);
429
430
431 static std::string grid_scale_type_string = "constant";
432 pp.queryAdd("grid_scale_type",grid_scale_type_string);
433
434 if (amrex::toLower(grid_scale_type_string) == "constant") {
436 } else if (amrex::toLower(grid_scale_type_string) == "custom") {
437 amrex::Warning("Initialization of grid scale from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
439 } else if (amrex::toLower(grid_scale_type_string) == "analytic") {
441 } else {
442 amrex::Error("Don't know this grid_scale_type");
443 }
444
445 static std::string ic_type_string = "analytic";
446 bool found_ic_bc = pp.queryAdd("ic_bc_type", ic_type_string);
447 pp.queryAdd("ic_type", ic_type_string);
448
449 if (found_ic_bc) {
450 amrex::Warning("remora.ic_bc_type is now called remora.ic_type, and will eventually be deprecated");
451 }
452
453 if ( amrex::toLower(ic_type_string) == "custom") {
454 amrex::Warning("Problem initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
456 } else if ( amrex::toLower(ic_type_string) == "analytic") {
458 } else if ( amrex::toLower(ic_type_string) == "netcdf") {
460 } else if ( amrex::toLower(ic_type_string) == "real") {
461 amrex::Warning("Problem initialization from NetCDF (remora.ic_type) is now called 'netcdf'. 'real' will be deprecated");
463 } else {
464 amrex::Error("Don't know this ic_type");
465 }
466
467#ifndef REMORA_USE_NETCDF
468 // Without this the netcdf branches are compiled out one by one and the run integrates
469 // uninitialized state instead of failing: the #ifdef in REMORA::init_only sits inside
470 // the ic_type == netcdf branch rather than around it. mask_type below defaults to
471 // netcdf for a netcdf run, which would be silently skipped the same way.
472 if (ic_type == IC_Type::netcdf) {
473 amrex::Abort("remora.ic_type = netcdf requires a NetCDF build. Rebuild with "
474 "USE_PNETCDF=TRUE (GNUmake) or -DREMORA_ENABLE_PNETCDF=ON (CMake)");
475 }
476#endif
477
478 // Which type of refinement
479 static std::string coupling_type_string = "TwoWay";
480 pp.queryAdd("coupling_type",coupling_type_string);
481 if (amrex::toLower(coupling_type_string) == "twoway" ||
482 amrex::toLower(coupling_type_string) == "two_way") {
484 } else if (amrex::toLower(coupling_type_string) == "oneway" ||
485 amrex::toLower(coupling_type_string) == "one_way") {
487 } else {
488 amrex::Abort("Dont know this coupling_type");
489 }
490
491 // Which type of coriolis forcing
492 if (use_coriolis) {
493 static std::string coriolis_type_string = "beta_plane";
494 pp.queryAdd("coriolis_type",coriolis_type_string);
495 if ( amrex::toLower(coriolis_type_string) == "custom") {
496 amrex::Warning("Coriolis initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
498 } else if ( amrex::toLower(coriolis_type_string) == "analytic") {
500 } else if ((amrex::toLower(coriolis_type_string) == "beta_plane") ||
501 (amrex::toLower(coriolis_type_string) == "betaplane")) {
503 } else if ( (amrex::toLower(coriolis_type_string) == "netcdf")) {
505 } else if ( (amrex::toLower(coriolis_type_string) == "real")) {
506 amrex::Warning("Coriolis initialization from NetCDF is now called 'netcdf'. 'real' will be deprecated");
508 } else {
509 amrex::Abort("Don't know this coriolis_type");
510 }
511 }
512
513 static std::string smflux_type_string = "analytic";
514 int smflux_specified = pp.queryAdd("smflux_type",smflux_type_string);
515 if ( amrex::toLower(smflux_type_string) == "custom") {
516 amrex::Warning("Surface momentum flux initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
518 } else if ( amrex::toLower(smflux_type_string) == "analytic") {
520 } else if ( amrex::toLower(smflux_type_string) == "netcdf") {
522 } else {
523 amrex::Abort("Don't know this smflux_type");
524 }
525
526 static std::string wind_type_string = "analytic";
527 int wind_specified = pp.queryAdd("wind_type",wind_type_string);
528 if ( amrex::toLower(wind_type_string) == "custom") {
529 amrex::Warning("Surface wind initialization from prob.cpp is now called 'analytic'. 'custom' will be deprecated");
531 } else if ( amrex::toLower(wind_type_string) == "analytic") {
533 } else if ( amrex::toLower(wind_type_string) == "netcdf") {
535 } else {
536 amrex::Abort("Don't know this smflux_type");
537 }
538
539 if (wind_specified) {
542 // wind_type names both wind lanes, so it counts as specifying them
543 // even with the per-lane keys absent.
548 }
551 }
552 }
553
555 amrex::Abort("Cannot specify both wind and surface momentum flux");
556 }
557
558 static std::string mask_type_string = "none";
559 // If initial condition type is netcdf, default to netcdf masks
560 if (ic_type == IC_Type::netcdf) {
561 mask_type_string = "netcdf";
562 }
563 pp.queryAdd("mask_type", mask_type_string);
564 if (amrex::toLower(mask_type_string) == "none") {
566 } else if (amrex::toLower(mask_type_string) == "analytic") {
568 } else if (amrex::toLower(mask_type_string) == "netcdf") {
570 } else {
571 amrex::Abort("Don't know this mask_type");
572 }
573
574 // Off by default: the masks are derived from one specification, so this validates
575 // that machinery rather than user input. Worth paying for in the test suite and when
576 // bringing up a new grid, not in production.
577 pp.queryAdd("check_mask_consistency", do_check_mask_consistency);
578
579 static std::string mask_consistency_string = "abort";
580 pp.queryAdd("mask_consistency", mask_consistency_string);
581 if (amrex::toLower(mask_consistency_string) == "abort") {
583 } else if (amrex::toLower(mask_consistency_string) == "warn") {
585 } else {
586 amrex::Abort("Don't know this mask_consistency");
587 }
588
589 static std::string bottom_stress_type_string = "linear";
590 pp.queryAdd("bottom_stress_type", bottom_stress_type_string);
591 if (amrex::toLower(bottom_stress_type_string) == "linear") {
593 } else if (amrex::toLower(bottom_stress_type_string) == "quadratic") {
595 } else if (amrex::toLower(bottom_stress_type_string) == "logarithmic") {
597 } else {
598 amrex::Abort("Don't know this bottom_stress_type");
599 }
600
601 amrex::Real tnu2_salt = zero;
602 amrex::Real tnu2_temp = zero;
603 amrex::Real tnu2_scalar = zero;
604 static std::string horiz_mixing_type_string = "analytic";
605 pp.queryAdd("horizontal_mixing_type", horiz_mixing_type_string);
606 if (amrex::toLower(horiz_mixing_type_string) == "analytical" ||
607 amrex::toLower(horiz_mixing_type_string) == "analytic") {
609 } else if (amrex::toLower(horiz_mixing_type_string) == "constant") {
611 } else if (amrex::toLower(horiz_mixing_type_string) == "scaled_to_grid") {
613 } else {
614 amrex::Abort("Don't know this horizontal mixing type");
615 }
616 pp.queryAdd("visc2",visc2);
617 pp.queryAdd("tnu2_salt",tnu2_salt);
618 pp.queryAdd("tnu2_temp",tnu2_temp);
619 pp.queryAdd("tnu2_scalar",tnu2_scalar);
620
621 // For scaled_to_grid runs with AMR refinement: optionally scale the coefficients
622 // by the horizontal refinement ratio (linear in grid size).
623 static std::string scaled_to_grid_amr_scaling_string = "none";
624 pp.queryAdd("scaled_to_grid_amr_scaling", scaled_to_grid_amr_scaling_string);
625 if (amrex::toLower(scaled_to_grid_amr_scaling_string) == "none") {
627 } else if (amrex::toLower(scaled_to_grid_amr_scaling_string) == "linear") {
629 } else {
630 amrex::Abort("Don't know this scaled_to_grid_amr_scaling option");
631 }
632
633 // Horizontal diffusivity is per tracer in ROMS, so mirror that here:
634 // remora.tnu2_scalar sets the group default and remora.tnu2_{var} overrides a single
635 // tracer by name -- remora.tnu2_NO3, remora.tnu2_tracer_1, and so on.
636 //
637 // tnu2_temp and tnu2_salt are the pre-existing spelling of that per-name form for the
638 // first two components. They keep their own defaults of zero rather than inheriting
639 // tnu2_scalar, so every existing input file means exactly what it did before, and the
640 // loop below skips them to avoid querying the same key twice.
641 tnu2.assign(ncons, tnu2_scalar);
642 if (ncons > Temp_comp) {
644 }
645 if (ncons > Salt_comp) {
647 }
648 for (int icomp = Tracer_comp; icomp < ncons; ++icomp) {
649 pp.queryAdd(("tnu2_" + cons_names[icomp]).c_str(), tnu2[icomp]);
650 }
651
652 // Vertical diffusivity is not. ROMS computes and stores Akt for the active tracers
653 // only, and every passive tracer -- dye and biology alike -- mixes with the salinity
654 // coefficient, so Akt_bak has NAT entries: remora.Akt_bak sets both and
655 // remora.Akt_bak_temp / remora.Akt_bak_salt override one.
656 Akt_bak.assign(NAT, Akt_bak_all);
657 for (int icomp = 0; icomp < NAT; ++icomp) {
658 pp.queryAdd(("Akt_bak_" + cons_names[icomp]).c_str(), Akt_bak[icomp]);
659 }
660 // A per-tracer key would name a coefficient nothing reads, and reading as though it
661 // had been applied is worse than not offering it, so say so rather than ignoring it.
662 for (int icomp = Tracer_comp; icomp < ncons; ++icomp) {
663 const std::string key = "Akt_bak_" + cons_names[icomp];
664 if (pp.contains(key.c_str())) {
665 amrex::Abort("remora." + key + " has no effect: vertical diffusivity is carried"
666 " for temperature and salinity only, and every passive tracer mixes"
667 " with the salinity value. Set remora.Akt_bak_salt instead, or"
668 " remora.tnu2_" + cons_names[icomp] + " if you meant the horizontal"
669 " diffusivity.");
670 }
671 }
672
673 static std::string harmonic_mixing_type_string = "s";
674 pp.queryAdd("harmonic_mixing_type", harmonic_mixing_type_string);
675 if (amrex::toLower(harmonic_mixing_type_string) == "s") {
677 } else if (amrex::toLower(harmonic_mixing_type_string) == "geopotential" ||
678 amrex::toLower(harmonic_mixing_type_string) == "geo") {
680 } else {
681 amrex::Abort("Don't know this harmonic_mixing_type");
682 }
683
684 pp.queryAdd("Akk_bak", Akk_bak);
685 pp.queryAdd("Akp_bak", Akp_bak);
686 static std::string vert_mixing_type_string = "analytic";
687 static std::string gls_stability_type_string = "Canuto_A";
688 pp.queryAdd("vertical_mixing_type", vert_mixing_type_string);
689 pp.queryAdd("gls_stability_type", gls_stability_type_string);
690 if (amrex::toLower(vert_mixing_type_string) == "analytical" ||
691 amrex::toLower(vert_mixing_type_string) == "analytic") {
693 } else if (amrex::toLower(vert_mixing_type_string) == "gls") {
695 if (amrex::toLower(gls_stability_type_string) == "canuto_a") {
697 }
698 else if (amrex::toLower(gls_stability_type_string) == "canuto_b") {
700 }
701 else if (amrex::toLower(gls_stability_type_string) == "galperin") {
703 }
704 else {
705 amrex::Abort("Don't know this GLS stability type");
706 }
707 } else {
708 amrex::Abort("Don't know this vertical mixing type");
709 }
710 // Read in GLS params
712 pp.queryAdd("gls_P", gls_p);
713 pp.queryAdd("gls_M", gls_m);
714 pp.queryAdd("gls_N", gls_n);
715 pp.queryAdd("gls_Kmin", gls_Kmin);
716 pp.queryAdd("gls_Pmin", gls_Pmin);
717
718 pp.queryAdd("gls_cmu0", gls_cmu0);
719 pp.queryAdd("gls_c1", gls_c1);
720 pp.queryAdd("gls_c2", gls_c2);
721 pp.queryAdd("gls_c3m", gls_c3m);
722 pp.queryAdd("gls_c3p", gls_c3p);
723 pp.queryAdd("gls_sigk", gls_sigk);
724 pp.queryAdd("gls_sigp", gls_sigp);
726 gls_Gh0 = amrex::Real(0.0329); // 0.0329 GOTM, 0.0673 Burchard
727 gls_Ghcri = amrex::Real(0.03);
728 gls_L1 = amrex::Real(0.107);
729 gls_L2 = amrex::Real(0.0032);
730 gls_L3 = amrex::Real(0.0864);
731 gls_L4 = amrex::Real(0.12);
732 gls_L5 = amrex::Real(11.9);
733 gls_L6 = amrex::Real(0.4);
734 gls_L7 = zero;
735 gls_L8 = amrex::Real(0.48);
737 gls_Gh0 = amrex::Real(0.0444); // 0.044 GOTM, 0.0673 Burchard
738 gls_Ghcri = amrex::Real(0.0414);
739 gls_L1 = amrex::Real(0.127);
740 gls_L2 = amrex::Real(0.00336);
741 gls_L3 = amrex::Real(0.0906);
742 gls_L4 = amrex::Real(0.101);
743 gls_L5 = amrex::Real(11.2);
744 gls_L6 = amrex::Real(0.4);
745 gls_L7 = zero;
746 gls_L8 = amrex::Real(0.318);
747 } else {
748 gls_Gh0 = amrex::Real(0.028);
749 gls_Ghcri = amrex::Real(0.02);
750 }
751 }
752
753 // Read and compute inverse nudging coeffs from inputs given in days,
754 // and store in a vector corresponding to BdyVars enum
755 amrex::Real tnudg = zero;
756 amrex::Real znudg = zero;
757 amrex::Real m2nudg = zero;
758 amrex::Real m3nudg = zero;
759 pp.queryAdd("tnudg",tnudg);
760 pp.queryAdd("znudg",znudg);
761 pp.queryAdd("m2nudg",m2nudg);
762 pp.queryAdd("m3nudg",m3nudg);
763 pp.queryAdd("obcfac",obcfac);
764
765 nudg_coeff.resize(BdyVars::NumTypes(ncons));
766 nudg_coeff[BdyVars::u ] = (m3nudg > zero) ? one / (m3nudg * amrex::Real(86400.0)) : zero;//BdyVars::u
767 nudg_coeff[BdyVars::v ] = (m3nudg > zero) ? one / (m3nudg * amrex::Real(86400.0)) : zero;//BdyVars::v
768 // Every cell-centered tracer -- temp, salt, and any additional passive or
769 // biology scalar -- nudges on the single tracer timescale tnudg, as in ROMS
770 // where Tnudg defaults to the same value for all tracers.
771 for (int icomp = 0; icomp < ncons; ++icomp) {
772 nudg_coeff[BdyVars::cons(icomp)] = (tnudg > zero) ? one / (tnudg * amrex::Real(86400.0)) : zero;
773 }
774 nudg_coeff[BdyVars::ubar(ncons)] = (m2nudg > zero) ? one / (m2nudg * amrex::Real(86400.0)) : zero;//BdyVars::ubar
775 nudg_coeff[BdyVars::vbar(ncons)] = (m2nudg > zero) ? one / (m2nudg * amrex::Real(86400.0)) : zero;//BdyVars::vbar
776 nudg_coeff[BdyVars::zeta(ncons)] = ( znudg > zero) ? one / ( znudg * amrex::Real(86400.0)) : zero;//BdyVars::zeta
777
778 pp.queryAdd("do_m3_clim_nudg", do_m3_clim_nudg);
779 pp.queryAdd("do_m2_clim_nudg", do_m2_clim_nudg);
780
781 // Every cell-centered tracer -- temp, salt, and any additional passive or biology
782 // scalar -- can be nudged toward its own climatology. The flag is keyed by the
783 // variable's own name, so temp and salt keep do_temp_clim_nudg / do_salt_clim_nudg
784 // and a biology tracer uses e.g. do_NO3_clim_nudg.
785 do_cons_clim_nudg.assign(ncons, 0);
786 for (int icomp = 0; icomp < ncons; ++icomp) {
787 bool do_nudg = false;
788 pp.queryAdd(("do_" + cons_names[icomp] + "_clim_nudg").c_str(), do_nudg);
790 if (do_nudg) { do_any_cons_clim_nudg = true; }
791 }
792
794 do_any_clim_nudg = true;
795 }
796 // The climatology series are only built along the NetCDF initialization path,
797 // so asking for nudging with analytic initial conditions would leave them
798 // unallocated. Say so here rather than failing later inside the time step.
800 amrex::Abort("Climatology nudging requires remora.ic_type = netcdf");
801 }
802#ifndef REMORA_USE_NETCDF
803 if (do_any_clim_nudg) {
804 amrex::Abort("Climatology nudging requires building with NetCDF");
805 }
806#endif
807 }
808
809 void display()
810 {
811 amrex::Print() << "SOLVER CHOICE: " << std::endl;
812 amrex::Print() << "use_salt : " << use_salt << std::endl;
813 amrex::Print() << "use_coriolis : " << use_coriolis << std::endl;
814 amrex::Print() << "use_prestep : " << use_prestep << std::endl;
815 amrex::Print() << "use_uv3dmix : " << use_uv3dmix << std::endl;
816 amrex::Print() << "spatial_order : " << spatial_order << std::endl;
817
818 if (ic_type == IC_Type::analytic) {
819 amrex::Print() << "Using analytic initial onditions" << std::endl;
820 }
821 else if (ic_type == IC_Type::netcdf) {
822 amrex::Print() << "Using NetCDF initial conditions" << std::endl;
823 }
824
826 amrex::Print() << "Horizontal advection scheme for tracers: " << "Centered 4" << std::endl;
827 }
829 amrex::Print() << "Horizontal advection scheme for tracers: " << "Upstream 3" << std::endl;
830 }
831 else {
832 amrex::Error("Invalid horizontal advection scheme for tracers.");
833 }
834
836 amrex::Print() << "Horizontal advection scheme for momenta: " << "Centered 2" << std::endl;
837 }
839 amrex::Print() << "Horizontal advection scheme for momenta: " << "Upstream 3" << std::endl;
840 }
841 else {
842 amrex::Error("Invalid horizontal advection scheme for momenta.");
843 }
844
846 amrex::Print() << "Using two-way coupling " << std::endl;
847 } else if (coupling_type == CouplingType::one_way) {
848 amrex::Print() << "Using one-way coupling " << std::endl;
849 }
850
851 if (use_coriolis) {
853 amrex::Print() << "Using analytic coriolis forcing " << std::endl;
854 } else if (coriolis_type == Cor_Type::beta_plane) {
855 amrex::Print() << "Using beta plane coriolis forcing " << std::endl;
856 } else if (coriolis_type == Cor_Type::netcdf) {
857 amrex::Print() << "Using coriolis forcing loaded from file " << std::endl;
858 }
859 }
860 }
861
862 // Default prefix
863 std::string pp_prefix {"remora"};
864
865 bool use_salt = true;
866
867 // Specify what additional physics/forcing modules we use
868 bool use_coriolis = false;
869
870 // Specify whether terms are used for debugging purposes
871 bool use_prestep = true;
872 bool use_uv3dmix = true;
873 bool use_baroclinic = true;
874
876
877 bool bulk_fluxes = false;
878 bool atm2ocn_flux_mode = false;
879
880 bool output_forcing = false;
881 bool do_temp_flux = false;
882 bool do_salt_flux = false;
883 bool longwave_down = false;
884
885 std::array<BulkForcingType, BulkFlux::NumTypes> bulk_flux_type {{
894 BulkForcingType::computed, // LWrad defaults to the internal longwave formula
895 BulkForcingType::computed // EminusP defaults to bulk evap-rain diagnostic
896 }};
897
898 // Which bulk-flux inputs the deck actually named, rather than defaulted to.
899 //
900 // Not recoverable afterwards: init_params reads these with queryAdd, which
901 // writes the default back into the table on a miss, so contains() then says
902 // "yes" for all of them. A coupling driver needs the distinction to decide
903 // whether to override a lane, so it is recorded here during parsing.
904 std::array<bool, BulkFlux::NumTypes> bulk_flux_type_specified {};
905 std::array<bool, BulkFlux::NumTypes> bulk_flux_value_specified {};
906
907 bool longwave_is_net = false;
908 bool qair_is_percent = false;
909 std::string longwave_netcdf_varname = "lwrad";
910
911
912 bool do_rivers = false;
913 bool do_rivers_temp = true;
914 bool do_rivers_salt = true;
915 bool do_rivers_scalar = false;
916 amrex::Vector<int> do_rivers_cons;
917
918 bool init_l1ad_T = false;
919
920 bool init_ana_T = false;
921
922 bool init_l0int_T = true;
923
926
927 // Coupling options are "OneWay" or "TwoWay"
929
930 // IC and BC Type: "analytic" or "netcdf"
932
933 // Coriolis forcing type
935
936 // Surface momentum flux type
938
939 // Surface wind speed type
941
942 // EOS type
944
945 // Bottom stress type
947
948 // Land/sea mask type
950
951 // Whether to validate the land/sea masks at all, and what to do when they fail
954
955 // Mixing type and parameters
961
962 // Type for grid scale (pm and pn)
964
965 // Stretching and depth parameters which may need to be read from inputs
966 amrex::Real theta_s = amrex::Real(3.0);
967 amrex::Real theta_b = zero;
968 amrex::Real tcline = amrex::Real(150.0);
969
970 // Linear drag coefficient [m/s]
971 amrex::Real rdrag = amrex::Real(3e-4);
972 // Quadratic drag coefficient [dimensionless]
973 amrex::Real rdrag2 = amrex::Real(3e-3);
974
975 // Momentum stress scales [m]
976 amrex::Real Zob = amrex::Real(2e-2);
977 amrex::Real Zos = amrex::Real(2e-2);
978
979 amrex::Real Cdb_max = amrex::Real(0.5);
980 amrex::Real Cdb_min = amrex::Real(1e-6);
981
982 // Linear equation of state parameters
983 amrex::Real R0 = amrex::Real(1028); // background density value (Kg/m3) used in Linear Equation of State
984 amrex::Real S0 = amrex::Real(35.0); // background salinity (nondimensional) constant
985 amrex::Real T0 = amrex::Real(5.0); // background potential temperature (Celsius) constant
986 amrex::Real Tcoef = amrex::Real(1.7e-4); // linear equation of state parameter (1/Celsius)
987 amrex::Real Scoef = zero; // linear equation of state parameter (nondimensional)
988 amrex::Real rho0 = amrex::Real(1025.0); // Mean density (Kg/m3) used when Boussinesq approx is inferred
989 amrex::Real g = amrex::Real(9.80665); // acceleration due to gravity [m/s2]; remora.g
990
991 // remora.time_ref: reference date of the model clock (yyyymmdd.dd), or one
992 // of the special values 0, -1, -2. Selects the calendar; see
993 // Source/Utils/REMORA_DateClock.H.
994 // Always double, even in a single-precision build: a yyyymmdd.dd date needs
995 // eight exact digits, more than a float holds.
996 double time_ref = 0.0;
997
998 // Coriolis forcing
999 amrex::Real coriolis_f0 = zero; // f-plane constant (1/s)
1000 amrex::Real coriolis_beta = zero; // beta-plane constant (1/s/m)
1001
1002 // Air pressure
1003 amrex::Real Pair = amrex::Real(1013.48);
1004 // Air temperature
1005 amrex::Real Tair = amrex::Real(23.567);
1006 // Relative humidity (air)
1007 amrex::Real Hair = amrex::Real(0.776);
1008 // Cloud cover fraction (0=clear sky, 1=overcast)
1009 amrex::Real cloud = zero;
1010 // Precipitation rate (kg/m2/s)
1011 amrex::Real rain = zero;
1012 // Height (m) of atmospheric measurements for Bulk fluxes parametrization
1013 amrex::Real blk_ZQ = amrex::Real(10.0); // air humidity
1014 amrex::Real blk_ZT = amrex::Real(10.0); // air temperature
1015 amrex::Real blk_ZW = amrex::Real(10.0); // winds
1016
1017 bool eminusp = false;
1019
1020 // Surface radiation flux
1021 amrex::Real srflux = zero;
1022 // Surface wind speed
1023 amrex::Real Uwind = zero;
1024 amrex::Real Vwind = zero;
1025 // External longwave radiation flux
1026 amrex::Real longwave_rad = zero;
1027 // Prescribed evaporation minus precipitation
1028 amrex::Real EminusP = zero;
1029
1030 // Spatial discretization
1032
1033 // Horizontal mixing parameters
1034 amrex::Real visc2 = zero;
1035 amrex::Vector<amrex::Real> tnu2;
1036
1037 // GLS params
1038 amrex::Real gls_p = amrex::Real(3.0);
1039 amrex::Real gls_m = amrex::Real(1.5);
1040 amrex::Real gls_n = amrex::Real(-1.0);
1041 amrex::Real gls_Kmin = amrex::Real(7.6e-6);
1042 amrex::Real gls_Pmin = amrex::Real(1.0e-12);
1043
1044 amrex::Real gls_cmu0 = amrex::Real(0.5477);
1045 amrex::Real gls_c1 = amrex::Real(1.44);
1046 amrex::Real gls_c2 = amrex::Real(1.92);
1047 amrex::Real gls_c3m = amrex::Real(-0.4);
1048 amrex::Real gls_c3p = one;
1049 amrex::Real gls_sigk = one;
1050 amrex::Real gls_sigp = amrex::Real(1.3);
1051
1052 // Turbulence closure
1053 amrex::Real Akk_bak = amrex::Real(5.0e-6);
1054 amrex::Real Akp_bak = amrex::Real(5.0e-6);
1055 amrex::Real Akv_bak = amrex::Real(5.0e-6);
1056 // remora.Akt_bak: the value both active tracers take unless overridden by name.
1057 amrex::Real Akt_bak_all = amrex::Real(1.0e-6);
1058 // NAT entries -- temperature and salinity -- to match the components of vec_Akt.
1059 // Passive tracers mix with entry Salt_comp; see akt_comp().
1060 amrex::Vector<amrex::Real> Akt_bak;
1061
1062 // Params for stability functions.
1063 amrex::Real gls_Gh0;
1064 amrex::Real gls_Ghcri;
1065 amrex::Real gls_Ghmin = amrex::Real(-0.28);
1066 amrex::Real gls_E2 = amrex::Real(1.33);
1067 // Params only for Canuto stability
1068 amrex::Real gls_L1;
1069 amrex::Real gls_L2;
1070 amrex::Real gls_L3;
1071 amrex::Real gls_L4;
1072 amrex::Real gls_L5;
1073 amrex::Real gls_L6;
1074 amrex::Real gls_L7;
1075 amrex::Real gls_L8;
1076
1077 // Params for some GLS and also Mellor-Yamada
1078 amrex::Real my_A1 = amrex::Real(0.92);
1079 amrex::Real my_A2 = amrex::Real(0.74);
1080 amrex::Real my_B1 = amrex::Real(16.6);
1081 amrex::Real my_B2 = amrex::Real(10.1);
1082 amrex::Real my_C1 = amrex::Real(0.08);
1083 amrex::Real my_C2 = amrex::Real(0.7);
1084 amrex::Real my_C3 = amrex::Real(0.2);
1085 amrex::Real my_E1 = amrex::Real(1.8);
1086 amrex::Real my_E2 = amrex::Real(1.33);
1087 amrex::Real my_Gh0 = amrex::Real(0.0233);
1088 amrex::Real my_Sq = amrex::Real(0.2);
1089 amrex::Real my_dtfac = amrex::Real(0.05);
1090 amrex::Real my_lmax = amrex::Real(0.53);
1091 amrex::Real my_qmin = amrex::Real(1.0E-8);
1092
1093 // Nudging time scales in 1/s
1094 amrex::Vector<amrex::Real> nudg_coeff;
1095
1096 // Factor between passive (outflow) and active (inflow) open boundary
1097 // conditions.
1098 amrex::Real obcfac = zero;
1099
1100 // Whether to do climatoogy nudging
1101 bool do_m2_clim_nudg = false;
1102 bool do_m3_clim_nudg = false;
1103 // Per cell-centered tracer, whether to nudge toward climatology. Indexed by
1104 // cons component, so entry Temp_comp is the old do_temp_clim_nudg.
1105 amrex::Vector<int> do_cons_clim_nudg;
1107 bool do_any_clim_nudg = false;
1108
1110};
1111
1112#endif
constexpr amrex::Real one
constexpr amrex::Real zero
BottomStressType
bottom stress formulation
GridScaleType
initialization for pm and pn
MaskConsistency
what to do when a level's mask disagrees with the level above it
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(double time_ref) noexcept
Whether time_ref names a calendar this header implements.
#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 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
MaskConsistency mask_consistency
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
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 Cdb_max
GridScaleType grid_scale_type
amrex::Real my_qmin
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