ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_InitCustomPert_ABL.H
Go to the documentation of this file.
1 
2  ParmParse pp("prob");
3 
5  int fix_random_seed = 0;
6  long random_seed = -1;
7  {
8  amrex::ParmParse pp_erf("erf");
9  pp_erf.query("deterministic_ic_pert", deterministic_ic_pert);
10  pp_erf.query("fix_random_seed", fix_random_seed);
11  pp_erf.query("random_seed", random_seed);
12  }
13  const std::uint64_t ic_pert_seed = fix_random_seed
14  ? std::uint64_t(1024)
15  : ((random_seed >= 0) ? static_cast<std::uint64_t>(random_seed) : std::uint64_t(0));
16  constexpr int temperature_hash_comp = 0;
17 
18  Real rho_0 = amrex::Real(0); pp.query("rho_0", rho_0);
19  Real T_0 = amrex::Real(0); pp.query("T_0", T_0);
20  Real A_0 = amrex::Real(1); pp.query("A_0", A_0);
21  Real KE_0 = amrex::Real(0.1); pp.query("KE_0", KE_0);
22  Real rhoKE_0 = -1; pp.query("rhoKE_0", rhoKE_0);
23 
24  Real KE_decay_height = -1; pp.query("KE_decay_height", KE_decay_height);
25  Real KE_decay_order = 1; pp.query("KE_decay_order", KE_decay_order);
26 
27  // random initial perturbations (legacy code)
28  Real T_0_Pert_Mag = amrex::Real(0); pp.query("T_0_Pert_Mag", T_0_Pert_Mag);
29  bool pert_rhotheta = true; pp.query("pert_rhotheta", pert_rhotheta);
30 
31  // divergence-free initial perturbations
32  Real pert_deltaU = amrex::Real(0); pp.query("pert_deltaU", pert_deltaU);
33  Real pert_deltaV = amrex::Real(0); pp.query("pert_deltaV", pert_deltaV);
34  Real pert_ref_height = amrex::Real(100.0); pp.query("pert_ref_height", pert_ref_height);
35 
36  // sinusoidal perturbations for regression tests. pert_periods_T defaults to
37  // 5 to match pert_periods_U/V in ERF_InitCustomPertVels_ABL.H: a zero period
38  // would make cos(cval*(x-xc)) identically one, i.e. a uniform warm bias
39  // rather than a zero-mean perturbation.
40  Real pert_deltaT = amrex::Real(0); pp.query("pert_deltaT", pert_deltaT);
41  Real pert_periods_T = amrex::Real(5.0); pp.query("pert_periods_T", pert_periods_T);
42 
43  // Capture by value for GPU
44  const Real dx = geomdata.CellSize(0);
45  const Real dy = geomdata.CellSize(1);
46  const Real prob_lo_x = geomdata.ProbLo(0);
47  const Real prob_lo_y = geomdata.ProbLo(1);
48  const Real prob_lo_z = geomdata.ProbLo(2);
49  const Real prob_hi_x = geomdata.ProbHi(0);
50  const Real prob_hi_y = geomdata.ProbHi(1);
51  const Real prob_hi_z = geomdata.ProbHi(2);
52 
53  // Define a point (xc,yc,zc) at the center of the domain
54  const Real xc = myhalf * (prob_lo_x + prob_hi_x);
55  const Real yc = myhalf * (prob_lo_y + prob_hi_y);
56  const Real zc = myhalf * (prob_lo_z + prob_hi_z);
57 
58  // Sinusoidal perturbation wavenumber and amplitude factor. These mirror
59  // the divergence-free velocity perturbations in ERF_InitCustomPertVels_ABL.H
60  // so that tfac*damp*z peaks at pert_deltaT when z = pert_ref_height.
61  // pert_ref_height <= 0 is used elsewhere in this file to mean "no
62  // perturbations", so guard the division: an infinite tfac would meet a zero
63  // damp inside the kernel and yield NaN under amrex.fpe_trap_invalid.
64  const bool add_sinusoidal_T = (pert_deltaT != amrex::Real(0)) &&
68  ? pert_deltaT * std::exp(myhalf) / pert_ref_height
69  : amrex::Real(0);
70 
71  const bool use_moisture = (sc.moisture_type != MoistureType::None);
72  const Real rdOcp = sc.rdOcp;
73 
74  if (KE_decay_height > 0) {
75  amrex::Print() << "Initial KE profile (order " << KE_decay_order
76  << ") will extend up to " << KE_decay_height
77  << std::endl;
78  }
79 
80 #ifndef AMREX_USE_GPU
81  if (pert_ref_height > 0) {
82  if (T_0_Pert_Mag != amrex::Real(0)) {
83  if (pert_rhotheta) {
84  amrex::Print() << "Adding random rho*theta perturbations" << std::endl;
85  } else {
86  amrex::Print() << "Adding random theta perturbations" << std::endl;
87  }
88  }
89  if (add_sinusoidal_T) {
90  amrex::Print() << "Adding sinusoidal temperature perturbations "
91  << pert_deltaT << " over " << pert_periods_T
92  << " periods" << std::endl;
93  }
94  }
95  // The sinusoidal temperature perturbation is carried entirely by the density
96  // perturbation, which ERF_InitCustomPertState.cpp drops when the level uses a
97  // fixed-in-time density (implied by erf.anelastic). Say so rather than fail
98  // silently, since the companion velocity perturbations still apply.
99  if (add_sinusoidal_T && sc.fixed_density[lev]) {
100  amrex::Print() << "WARNING: prob.pert_deltaT has no effect at level " << lev
101  << " because fixed_density is set there" << std::endl;
102  }
103 #endif
104 
105  ParallelForRNG(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k, const amrex::RandomEngine& engine) noexcept
106  {
107  const Real x = prob_lo_x + (i + myhalf) * dx;
108  const Real y = prob_lo_y + (j + myhalf) * dy;
109  const Real z = z_cc(i,j,k);
110 
111  const Real r = std::sqrt((x-xc)*(x-xc) + (y-yc)*(y-yc) + (z-zc)*(z-zc));
112 
113  // Add temperature perturbations
114  if ((z <= pert_ref_height) && (T_0_Pert_Mag != amrex::Real(0))) {
115  Real rand_double;
116  if (deterministic_ic_pert) {
117  rand_double = erf_hash_rng::hash_uniform(
118  i, j, k, temperature_hash_comp, lev, ic_pert_seed);
119  } else {
120  rand_double = amrex::Random(engine); // Between zero and one
121  }
122  state_pert(i, j, k, RhoTheta_comp) = (rand_double*amrex::Real(2) - amrex::Real(1))*T_0_Pert_Mag;
123  if (!pert_rhotheta) {
124  // we're perturbing theta, not rho*theta
125  state_pert(i, j, k, RhoTheta_comp) *= r_hse(i,j,k);
126  }
127  }
128 
129  // Sinusoidal temperature perturbation for regression tests. Unlike the
130  // random branch above, this is a pure function of position, so the initial
131  // state does not depend on the box decomposition, the MPI rank count, or
132  // the OpenMP tile size -- which is what a bitwise gold-file comparison
133  // requires.
134  //
135  // The perturbation is isobaric: T is perturbed at fixed pressure, so it is
136  // carried by the density perturbation while rho*theta stays unchanged.
137  // Because P = P(rho*theta, qv), holding P fixed also requires qv to be
138  // unchanged, so RhoQ1 has to be rescaled to track the new density --
139  // otherwise qv = RhoQ1/rho drifts and drags the pressure along with it.
140  //
141  // This deliberately differs from ERF_InitCustomPert_SDMCongestus3D.H, which
142  // assigns state_pert(RhoTheta_comp) = 0 at this point: that would discard
143  // any random perturbation written by the T_0_Pert_Mag branch above. Leaving
144  // RhoTheta_comp alone is equivalent whenever T_0_Pert_Mag == 0 (state_pert is
145  // zero-initialized by the caller) and non-destructive when it is not -- but
146  // setting both knobs does perturb rho*theta, and the result is then no
147  // longer isobaric.
148  if (add_sinusoidal_T) {
149  const Real zl = z / pert_ref_height;
150  const Real damp = std::exp(-myhalf * zl * zl);
151 
152  const Real rho = state(i,j,k,Rho_comp);
153  const Real rhotheta = state(i,j,k,RhoTheta_comp);
154  const Real qv = (use_moisture) ? state(i,j,k,RhoQ1_comp) / rho : amrex::Real(0);
155 
156  const Real Told = getTgivenRandRTh(rho,rhotheta,qv);
157  const Real P = getPgivenRTh(rhotheta,qv);
158 
159  const Real Tnew = Told + tfac * damp * z * std::cos(cval * (x - xc));
160  const Real theta_new = getThgivenTandP(Tnew,P,rdOcp);
161  const Real rho_new = getRhogivenThetaPress(theta_new,P,rdOcp,qv);
162 
163  state_pert(i, j, k, Rho_comp) += rho_new - rho;
164  if (use_moisture) {
165  // Keep qv, and therefore P, fixed as the density changes.
166  state_pert(i, j, k, RhoQ1_comp) += (rho_new - rho) * qv;
167  }
168  }
169 
170  // Set scalar = A_0*exp(-10r^2), where r is distance from center of domain
171  state_pert(i, j, k, RhoScalar_comp) = A_0 * std::exp(-amrex::Real(10.)*r*r);
172 
173  // Set an initial value for SGS KE
174  if (state_pert.nComp() > RhoKE_comp) {
175  // Deardorff
176  if (rhoKE_0 > 0) {
177  state_pert(i, j, k, RhoKE_comp) = rhoKE_0;
178  } else {
179  state_pert(i, j, k, RhoKE_comp) = r_hse(i,j,k) * KE_0;
180  }
181  if (KE_decay_height > 0) {
182  // scale initial SGS kinetic energy with height
183  state_pert(i, j, k, RhoKE_comp) *= amrex::max(
184  std::pow(1 - amrex::min(z/KE_decay_height,amrex::Real(1)), KE_decay_order), Real(1e-12));
185  }
186  }
187  });
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
constexpr amrex::Real PI
Definition: ERF_Constants.H:42
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhogivenThetaPress(const amrex::Real th, const amrex::Real p, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:96
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getThgivenTandP(const amrex::Real T, const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:18
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:43
#define Rho_comp
Definition: ERF_IndexDefines.H:39
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45
#define RhoKE_comp
Definition: ERF_IndexDefines.H:41
const Real prob_lo_z
Definition: ERF_InitCustomPert_ABL.H:48
const Real prob_lo_y
Definition: ERF_InitCustomPert_ABL.H:47
int deterministic_ic_pert
Definition: ERF_InitCustomPert_ABL.H:4
Real T_0_Pert_Mag
Definition: ERF_InitCustomPert_ABL.H:28
const bool add_sinusoidal_T
Definition: ERF_InitCustomPert_ABL.H:64
Real pert_ref_height
Definition: ERF_InitCustomPert_ABL.H:34
const bool use_moisture
Definition: ERF_InitCustomPert_ABL.H:71
Real KE_0
Definition: ERF_InitCustomPert_ABL.H:21
const Real dy
Definition: ERF_InitCustomPert_ABL.H:45
Real rho_0
Definition: ERF_InitCustomPert_ABL.H:18
const Real zc
Definition: ERF_InitCustomPert_ABL.H:56
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72
Real rhoKE_0
Definition: ERF_InitCustomPert_ABL.H:22
const Real yc
Definition: ERF_InitCustomPert_ABL.H:55
ParmParse pp("prob")
const Real prob_lo_x
Definition: ERF_InitCustomPert_ABL.H:46
constexpr int temperature_hash_comp
Definition: ERF_InitCustomPert_ABL.H:16
long random_seed
Definition: ERF_InitCustomPert_ABL.H:6
Real T_0
Definition: ERF_InitCustomPert_ABL.H:19
Real pert_deltaV
Definition: ERF_InitCustomPert_ABL.H:33
const Real prob_hi_y
Definition: ERF_InitCustomPert_ABL.H:50
const std::uint64_t ic_pert_seed
Definition: ERF_InitCustomPert_ABL.H:13
const Real tfac
Definition: ERF_InitCustomPert_ABL.H:67
const Real prob_hi_x
Definition: ERF_InitCustomPert_ABL.H:49
const Real cval
Definition: ERF_InitCustomPert_ABL.H:66
Real KE_decay_order
Definition: ERF_InitCustomPert_ABL.H:25
Real pert_deltaU
Definition: ERF_InitCustomPert_ABL.H:32
const Real prob_hi_z
Definition: ERF_InitCustomPert_ABL.H:51
Real KE_decay_height
Definition: ERF_InitCustomPert_ABL.H:24
bool pert_rhotheta
Definition: ERF_InitCustomPert_ABL.H:29
ParallelForRNG(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k, const amrex::RandomEngine &engine) noexcept { const Real x=prob_lo_x+(i+myhalf) *dx;const Real y=prob_lo_y+(j+myhalf) *dy;const Real z=z_cc(i, j, k);const Real r=std::sqrt((x-xc) *(x-xc)+(y-yc) *(y-yc)+(z-zc) *(z-zc));if((z<=pert_ref_height) &&(T_0_Pert_Mag !=amrex::Real(0))) { Real rand_double;if(deterministic_ic_pert) { rand_double=erf_hash_rng::hash_uniform(i, j, k, temperature_hash_comp, lev, ic_pert_seed);} else { rand_double=amrex::Random(engine);} state_pert(i, j, k, RhoTheta_comp)=(rand_double *amrex::Real(2) - amrex::Real(1)) *T_0_Pert_Mag;if(!pert_rhotheta) { state_pert(i, j, k, RhoTheta_comp) *=r_hse(i, j, k);} } if(add_sinusoidal_T) { const Real zl=z/pert_ref_height;const Real damp=std::exp(-myhalf *zl *zl);const Real rho=state(i, j, k, Rho_comp);const Real rhotheta=state(i, j, k, RhoTheta_comp);const Real qv=(use_moisture) ? state(i, j, k, RhoQ1_comp)/rho :amrex::Real(0);const Real Told=getTgivenRandRTh(rho, rhotheta, qv);const Real P=getPgivenRTh(rhotheta, qv);const Real Tnew=Told+tfac *damp *z *std::cos(cval *(x - xc));const Real theta_new=getThgivenTandP(Tnew, P, rdOcp);const Real rho_new=getRhogivenThetaPress(theta_new, P, rdOcp, qv);state_pert(i, j, k, Rho_comp)+=rho_new - rho;if(use_moisture) { state_pert(i, j, k, RhoQ1_comp)+=(rho_new - rho) *qv;} } state_pert(i, j, k, RhoScalar_comp)=A_0 *std::exp(-amrex::Real(10.) *r *r);if(state_pert.nComp() > RhoKE_comp) { if(rhoKE_0 > 0) { state_pert(i, j, k, RhoKE_comp)=rhoKE_0;} else { state_pert(i, j, k, RhoKE_comp)=r_hse(i, j, k) *KE_0;} if(KE_decay_height > 0) { state_pert(i, j, k, RhoKE_comp) *=amrex::max(std::pow(1 - amrex::min(z/KE_decay_height, amrex::Real(1)), KE_decay_order), Real(1e-12));} } })
Real pert_periods_T
Definition: ERF_InitCustomPert_ABL.H:41
int fix_random_seed
Definition: ERF_InitCustomPert_ABL.H:5
Real A_0
Definition: ERF_InitCustomPert_ABL.H:20
const Real xc
Definition: ERF_InitCustomPert_ABL.H:54
Real pert_deltaT
Definition: ERF_InitCustomPert_ABL.H:40
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ P
Definition: ERF_IndexDefines.H:204
@ rho
Definition: ERF_Kessler.H:24
@ qv
Definition: ERF_Kessler.H:30
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real hash_uniform(int i, int j, int k, int comp, int lev, std::uint64_t seed) noexcept
Definition: ERF_HashRNG.H:68