ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_InitCustomPert_Bubble.H
Go to the documentation of this file.
1  // On a mesh other than ConstantDz, heights come from z_cc and the vertical levels,
2  // not from geomdata's prob_lo[2] and CellSize(2)
3  const bool use_z_phys = use_physical_z();
4 
5  ParmParse pp_prob("prob");
6 
7  Real T_0 = amrex::Real(300.0); pp_prob.query("T_0", T_0);
8  Real x_c = zero; pp_prob.query("x_c", x_c);
9  Real y_c = zero; pp_prob.query("y_c", y_c);
10  Real z_c = zero; pp_prob.query("z_c", z_c);
11  Real x_r = zero; pp_prob.query("x_r", x_r);
12  Real y_r = zero; pp_prob.query("y_r", y_r);
13  Real z_r = zero; pp_prob.query("z_r", z_r);
14 
15  Real T_pert = -amrex::Real(15.0); pp_prob.query("T_pert", T_pert);
16 
17  bool T_pert_is_airtemp = true; pp_prob.query("T_pert_is_airtemp", T_pert_is_airtemp);
18  bool perturb_rho = true; pp_prob.query("perturb_rho", perturb_rho);
19 
20  bool do_moist_bubble = false; pp_prob.query("do_moist_bubble", do_moist_bubble);
21  do_moist_bubble &= (sc.moisture_type != MoistureType::None);
22 
23  Real theta_pert = two; pp_prob.query("theta_pert", theta_pert);
24 
25  const int khi = geomdata.Domain().bigEnd()[2];
26 
27  AMREX_ALWAYS_ASSERT(bx.length()[2] == khi+1);
28 
29  const Real dz = geomdata.CellSize()[2];
30  const Real rdOcp = sc.rdOcp;
31 
32  amrex::Print() << "Bubble delta T = " << T_pert << " K" << std::endl;
33  amrex::Print() << " centered at ("
34  << x_c << " " << y_c << " " << z_c << ")" << std::endl;
35  amrex::Print() << " with extent ("
36  << x_r << " " << y_r << " " << z_r << ")" << std::endl;
37 
38  if (T_0 <= 0)
39  {
40  amrex::Print() << "Ignoring T_0 = " << T_0
41  << ", background fields should have been initialized with erf.init_type"
42  << std::endl;
43  }
44 
45  Real qt_init = amrex::Real(0.02); pp_prob.query("qt_init", qt_init);
46 
47  Real eq_pot_temp = amrex::Real(320.0); pp_prob.query("eq_pot_temp",eq_pot_temp);
48 
49  bool use_empirical = false;
50  pp_prob.query("use_empirical_psat",use_empirical);
51 
52  if (do_moist_bubble) {
53  Vector<Real> h_r(khi+2);
54  Vector<Real> h_p(khi+2);
55  Vector<Real> h_t(khi+2);
56  Vector<Real> h_q_v(khi+2);
57 
58  Gpu::DeviceVector<Real> d_r(khi+2);
59  Gpu::DeviceVector<Real> d_p(khi+2);
60  Gpu::DeviceVector<Real> d_t(khi+2);
61  Gpu::DeviceVector<Real> d_q_v(khi+2);
62 
63  if (use_z_phys) {
64  HSEutils::init_isentropic_hse_no_terrain(h_t.data(), h_r.data(),h_p.data(),h_q_v.data(),m_zlevels_stag[lev].data(),khi,
66  } else {
67  HSEutils::init_isentropic_hse_no_terrain(h_t.data(), h_r.data(),h_p.data(),h_q_v.data(),dz,geomdata.ProbLo(2),khi,
69  }
70 
71  Gpu::copyAsync(Gpu::hostToDevice, h_r.begin(), h_r.end(), d_r.begin());
72  Gpu::copyAsync(Gpu::hostToDevice, h_p.begin(), h_p.end(), d_p.begin());
73  Gpu::copyAsync(Gpu::hostToDevice, h_t.begin(), h_t.end(), d_t.begin());
74  Gpu::copyAsync(Gpu::hostToDevice, h_q_v.begin(), h_q_v.end(), d_q_v.begin());
75 
76  Real* theta_back = d_t.data();
77  Real* p_back = d_p.data();
78  Real* q_v_back = d_q_v.data();
79 
80  //
81  // Do we split the initial condensate into cloud water and cloud ice? We ask the
82  // model for its cloud ice component rather than testing on the name of the
83  // model, since that component is -1 for every model that doesn't carry one.
84  // The partition itself uses the SAM formulation -- a linear ramp in temperature
85  // between tbgmin and tbgmax -- which is generic enough for any of the Eulerian
86  // models that do carry cloud ice.
87  //
88  // We exclude the Lagrangian models: they seed their particles from the cloud water
89  // we set here and then overwrite the Eulerian condensate from those particles,
90  // so splitting it now would lose the ice and shrink the initial droplets.
91  //
92  const int qci_comp = sc.moisture_indices.qi;
93  const bool is_eulerian = (Microphysics::modelType(sc.moisture_type)
94  == MoistureModelType::Eulerian);
95  const bool split_condensate = (qci_comp >= 0) && is_eulerian;
96 
97  ParallelFor(bx, [=,zero_d=zero,one_d=one,tbgmin_d=tbgmin,a_bg_d=a_bg]
98  AMREX_GPU_DEVICE(int i, int j, int k)
99  {
100  // Geometry (note we must include these here to get the data on device)
101  const auto prob_lo = geomdata.ProbLo();
102  const auto dx = geomdata.CellSize();
103  const Real x = prob_lo[0] + (i + myhalf) * dx[0];
104  const Real y = prob_lo[1] + (j + myhalf) * dx[1];
105  const Real z = use_z_phys ? z_cc(i,j,k) : prob_lo[2] + (k + myhalf) * dx[2];
106 
107  Real rad, delta_theta, theta_total, rho, RH;
108 
109  // Introduce the warm bubble. Assume that the bubble is pressure matched with the background
110  rad = zero_d;
111  if (x_r > 0) rad += amrex::Math::powi<2>((x - x_c)/x_r);
112  if (y_r > 0) rad += amrex::Math::powi<2>((y - y_c)/y_r);
113  if (z_r > 0) rad += amrex::Math::powi<2>((z - z_c)/z_r);
114  rad = std::sqrt(rad);
115 
116  if (rad <= amrex::Real(1)) {
117  delta_theta = theta_pert*amrex::Math::powi<2>(std::cos(PI*rad/two));
118  } else {
119  delta_theta = zero_d;
120  }
121 
122  theta_total = theta_back[k]*(delta_theta/amrex::Real(300.0) + 1);
123  Real T = getTgivenPandTh(p_back[k], theta_total, RdoCp);
124  rho = p_back[k]/(R_d*T*(one_d + RvoRd*q_v_back[k]));
126  Real q_v_hot = vapor_mixing_ratio(p_back[k], T, RH);
127 
128  // Compute background quantities
129  Real T_back = getTgivenPandTh(p_back[k], theta_back[k], RdoCp);
130  Real rho_back = p_back[k]/(R_d*T_back*(one_d + RvoRd*q_v_back[k]));
131 
132  // This version perturbs rho but not p
133  state_pert(i, j, k, RhoTheta_comp) = rho*theta_total - rho_back*theta_back[k]*(one_d + RvoRd*q_v_back[k]);
134  state_pert(i, j, k, Rho_comp) = rho - rho_back*(one_d + qt_init);
135 
136  // Set scalar = 0 everywhere
137  state_pert(i, j, k, RhoScalar_comp) = zero_d;
138 
139  // mean states
140  state_pert(i, j, k, RhoQ1_comp) = rho*q_v_hot;
141  state_pert(i, j, k, RhoQ2_comp) = rho*(qt_init - q_v_hot);
142 
143  // Cold microphysics are present
144  if (split_condensate) {
145  // Liquid fraction of the condensate: 1 above tbgmax, 0 below tbgmin
146  Real omn = std::max(zero_d,std::min(one_d,(T-tbgmin_d)*a_bg_d));
147  Real qn = state_pert(i, j, k, RhoQ2_comp);
148  state_pert(i, j, k, RhoQ2_comp) = qn * omn;
149  state_pert(i, j, k, qci_comp) = qn * (one_d - omn);
150  }
151  });
152 
153  // The kernel above reads d_r, d_p, d_t and d_q_v; wait for it before they are freed
154  Gpu::streamSynchronize();
155  } else {
156  ParallelFor(bx, [=,zero_d=zero] AMREX_GPU_DEVICE(int i, int j, int k)
157  {
158  // Geometry (note we must include these here to get the data on device)
159  const auto prob_lo = geomdata.ProbLo();
160  const auto dx = geomdata.CellSize();
161 
162  const Real x = prob_lo[0] + (i + myhalf) * dx[0];
163  const Real y = prob_lo[1] + (j + myhalf) * dx[1];
164  const Real z = use_z_phys ? z_cc(i,j,k) : prob_lo[2] + (k + myhalf) * dx[2];
165 
166  //perturb_rho_theta(x, y, z, p_hse(i,j,k), r_hse(i,j,k), rdOcp,
167  // state_pert(i, j, k, Rho_comp),
168  // state_pert(i, j, k, RhoTheta_comp));
169 
170  // Perturbation air temperature
171  // - The bubble is either cylindrical (for 2-D problems, if two
172  // radial extents are specified) or an ellipsoid (if all three
173  // radial extents are specified).
174  Real L = zero_d;
175  if (x_r > 0) L += amrex::Math::powi<2>((x - x_c)/x_r);
176  if (y_r > 0) L += amrex::Math::powi<2>((y - y_c)/y_r);
177  if (z_r > 0) L += amrex::Math::powi<2>((z - z_c)/z_r);
178  L = std::sqrt(L);
179  Real dT;
180  if (L > amrex::Real(1)) {
181  dT = zero_d;
182  } else {
183  dT = T_pert * amrex::Math::powi<2>(std::cos(PI*L/two));
184  }
185 
186  // Temperature that satisfies the EOS given the hydrostatically balanced (r,p)
187  const Real Tbar_hse = p_hse(i,j,k) / (R_d * r_hse(i,j,k));
188 
189  // Note: theta_perturbed is theta PLUS perturbation in theta
190  Real theta_perturbed;
191  if (T_pert_is_airtemp) {
192  // dT is air temperature
193  theta_perturbed = (Tbar_hse + dT)*std::pow(p_0/p_hse(i,j,k), rdOcp);
194  } else {
195  // dT is potential temperature
196  theta_perturbed = Tbar_hse*std::pow(p_0/p_hse(i,j,k), rdOcp) + dT;
197  }
198 
199  if (perturb_rho)
200  {
201  // this version perturbs rho but not p (i.e., rho*theta)
202  // - hydrostatic rebalance is needed (TODO: is this true?)
203  // - this is the approach taken in the density current problem
204  state_pert(i,j,k,Rho_comp) = getRhoThetagivenP(p_hse(i,j,k)) / theta_perturbed - r_hse(i,j,k);
205  state_pert(i,j,k,RhoTheta_comp) = zero_d; // i.e., hydrostatically balanced pressure stays const
206  }
207  else
208  {
209  // this version perturbs rho*theta (i.e., p) but not rho
210  state_pert(i,j,k,Rho_comp) = zero_d; // i.e., hydrostatically balanced density stays const
211  state_pert(i,j,k,RhoTheta_comp) = r_hse(i,j,k) * theta_perturbed - getRhoThetagivenP(p_hse(i,j,k));
212  }
213  });
214  } // do_moist_bubble
constexpr amrex::Real p_0
Definition: ERF_Constants.H:69
constexpr amrex::Real RvoRd
Definition: ERF_Constants.H:59
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:57
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhoThetagivenP(const amrex::Real p, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:172
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:57
#define Rho_comp
Definition: ERF_IndexDefines.H:53
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:67
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:66
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
const bool use_z_phys
Definition: ERF_InitCustomPert_Bubble.H:3
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
Real z_c
Definition: ERF_InitCustomPert_Bubble.H:10
bool T_pert_is_airtemp
Definition: ERF_InitCustomPert_Bubble.H:17
Real x_r
Definition: ERF_InitCustomPert_Bubble.H:11
bool do_moist_bubble
Definition: ERF_InitCustomPert_Bubble.H:20
const Real rdOcp
Definition: ERF_InitCustomPert_Bubble.H:30
Real x_c
Definition: ERF_InitCustomPert_Bubble.H:8
ParmParse pp_prob("prob")
do_moist_bubble &Real theta_pert
Definition: ERF_InitCustomPert_Bubble.H:23
Real T_0
Definition: ERF_InitCustomPert_Bubble.H:7
Real z_r
Definition: ERF_InitCustomPert_Bubble.H:13
Real y_c
Definition: ERF_InitCustomPert_Bubble.H:9
bool perturb_rho
Definition: ERF_InitCustomPert_Bubble.H:18
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
Real T_pert
Definition: ERF_InitCustomPert_Bubble.H:15
const Real dz
Definition: ERF_InitCustomPert_Bubble.H:29
Real y_r
Definition: ERF_InitCustomPert_Bubble.H:12
background fields should have been initialized with erf init_type<< std::endl;} Real qt_init=amrex::Real(0.02);pp_prob.query("qt_init", qt_init);Real eq_pot_temp=amrex::Real(320.0);pp_prob.query("eq_pot_temp", eq_pot_temp);bool use_empirical=false;pp_prob.query("use_empirical_psat", use_empirical);if(do_moist_bubble) { Vector< Real > h_r(khi+2);Vector< Real > h_p(khi+2);Vector< Real > h_t(khi+2);Vector< Real > h_q_v(khi+2);Gpu::DeviceVector< Real > d_r(khi+2);Gpu::DeviceVector< Real > d_p(khi+2);Gpu::DeviceVector< Real > d_t(khi+2);Gpu::DeviceVector< Real > d_q_v(khi+2);if(use_z_phys) { HSEutils::init_isentropic_hse_no_terrain(h_t.data(), h_r.data(), h_p.data(), h_q_v.data(), m_zlevels_stag[lev].data(), khi, qt_init, eq_pot_temp, use_empirical, false);} else { HSEutils::init_isentropic_hse_no_terrain(h_t.data(), h_r.data(), h_p.data(), h_q_v.data(), dz, geomdata.ProbLo(2), khi, qt_init, eq_pot_temp, use_empirical, false);} Gpu::copyAsync(Gpu::hostToDevice, h_r.begin(), h_r.end(), d_r.begin());Gpu::copyAsync(Gpu::hostToDevice, h_p.begin(), h_p.end(), d_p.begin());Gpu::copyAsync(Gpu::hostToDevice, h_t.begin(), h_t.end(), d_t.begin());Gpu::copyAsync(Gpu::hostToDevice, h_q_v.begin(), h_q_v.end(), d_q_v.begin());Real *theta_back=d_t.data();Real *p_back=d_p.data();Real *q_v_back=d_q_v.data();const int qci_comp=sc.moisture_indices.qi;const bool is_eulerian=(Microphysics::modelType(sc.moisture_type)==MoistureModelType::Eulerian);const bool split_condensate=(qci_comp > ParallelFor(bx, [=, zero_d=zero, one_d=one, tbgmin_d=tbgmin, a_bg_d=a_bg] AMREX_GPU_DEVICE(int i, int j, int k) { const auto prob_lo=geomdata.ProbLo();const auto dx=geomdata.CellSize();const Real x=prob_lo[0]+(i+myhalf) *dx[0];const Real y=prob_lo[1]+(j+myhalf) *dx[1];const Real z=use_z_phys ? z_cc(i, j, k) :prob_lo[2]+(k+myhalf) *dx[2];Real rad, delta_theta, theta_total, rho, RH;rad=zero_d;if(x_r > 0) rad+=amrex::Math::powi< 2 >((x - x_c)/x_r);if(y_r > 0) rad+=amrex::Math::powi< 2 >((y - y_c)/y_r);if(z_r > 0) rad+=amrex::Math::powi< 2 >((z - z_c)/z_r);rad=std::sqrt(rad);if(rad<=amrex::Real(1)) { delta_theta=theta_pert *amrex::Math::powi< 2 >(std::cos(PI *rad/two));} else { delta_theta=zero_d;} theta_total=theta_back[k] *(delta_theta/amrex::Real(300.0)+1);Real T=getTgivenPandTh(p_back[k], theta_total, RdoCp);rho=p_back[k]/(R_d *T *(one_d+RvoRd *q_v_back[k]));RH=compute_relative_humidity(use_empirical);Real q_v_hot=vapor_mixing_ratio(p_back[k], T, RH);Real T_back=getTgivenPandTh(p_back[k], theta_back[k], RdoCp);Real rho_back=p_back[k]/(R_d *T_back *(one_d+RvoRd *q_v_back[k]));state_pert(i, j, k, RhoTheta_comp)=rho *theta_total - rho_back *theta_back[k] *(one_d+RvoRd *q_v_back[k]);state_pert(i, j, k, Rho_comp)=rho - rho_back *(one_d+qt_init);state_pert(i, j, k, RhoScalar_comp)=zero_d;state_pert(i, j, k, RhoQ1_comp)=rho *q_v_hot;state_pert(i, j, k, RhoQ2_comp)=rho *(qt_init - q_v_hot);if(split_condensate) { Real omn=std::max(zero_d, std::min(one_d,(T-tbgmin_d) *a_bg_d));Real qn=state_pert(i, j, k, RhoQ2_comp);state_pert(i, j, k, RhoQ2_comp)=qn *omn;state_pert(i, j, k, qci_comp)=qn *(one_d - omn);} })
const GpuArray< Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_InitCustomPert_CloudChamber.H:33
Real eq_pot_temp
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:27
bool use_empirical
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:29
Real qt_init
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:28
Vector< Real > h_t(khi+2)
Gpu::DeviceVector< Real > d_t(khi+2)
Vector< Real > h_q_v(khi+2)
Gpu::DeviceVector< Real > d_p(khi+2)
Gpu::DeviceVector< Real > d_q_v(khi+2)
Vector< Real > h_r(khi+2)
Vector< Real > h_p(khi+2)
Gpu::DeviceVector< Real > d_r(khi+2)
constexpr amrex::Real a_bg
Definition: ERF_MicrophysicsConstants.H:55
constexpr amrex::Real tbgmin
Definition: ERF_MicrophysicsConstants.H:48
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real compute_relative_humidity(amrex::Real qv, amrex::Real T, amrex::Real P)
Compute relative humidity from water vapor mixing ratio.
Definition: ERF_PrognosticCloudFraction.H:37
amrex::Real Real
Definition: ERF_ShocInterface.H:19
static MoistureModelType modelType(const MoistureType a_moisture_type)
query if a specified moisture model is Eulerian or Lagrangian
Definition: ERF_Microphysics.H:136
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real vapor_mixing_ratio(const Real p_b, const Real T_b, const Real RH, const bool use_empirical, int which_zone)
Definition: ERF_HSEUtils.H:475
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void init_isentropic_hse_no_terrain(Real *theta, Real *r, Real *p, Real *q_v, const Real &dz, const Real &z_bot, const int &khi, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const bool T_from_theta=false, const Real z_tr_1=-one, const Real z_tr_2=-one, const Real theta_0=amrex::Real(300), const Real theta_tr=amrex::Real(300), const Real T_tr=amrex::Real(300))
Definition: ERF_HSEUtils.H:769
@ None
Definition: ERF_LSM_Types.H:6
@ rho
Definition: ERF_Kessler.H:25
@ qn
Definition: ERF_Morrison.H:36
@ T
Definition: ERF_IndexDefines.H:168