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