/home/runner/work/amr-wind/amr-wind/amr-wind/transport_models/TwoPhaseTransport.H Source File

AMR-Wind API: /home/runner/work/amr-wind/amr-wind/amr-wind/transport_models/TwoPhaseTransport.H Source File
AMR-Wind API v0.1.0
CFD solver for wind plant simulations
Loading...
Searching...
No Matches
TwoPhaseTransport.H
Go to the documentation of this file.
1#ifndef TWOPHASETRANSPORT_H
2#define TWOPHASETRANSPORT_H
3
6#include "AMReX_ParmParse.H"
7
8namespace amr_wind::transport {
9
13class TwoPhaseTransport : public TransportModel::Register<TwoPhaseTransport>
14{
15public:
16 static constexpr bool constant_properties = false;
17
18 static std::string identifier() { return "TwoPhaseTransport"; }
19
20 explicit TwoPhaseTransport(const CFDSim& sim)
21 : m_repo(sim.repo()), m_physics_mgr(sim.physics_manager())
22 {
23 amrex::ParmParse pp("transport");
24 pp.query("viscosity_fluid1", m_mu1);
25 pp.query("viscosity_fluid2", m_mu2);
26 pp.query("laminar_prandtl_fluid1", m_Pr1);
27 pp.query("laminar_prandtl_fluid2", m_Pr2);
28 pp.query("turbulent_prandtl", m_Prt);
29
30 // Check for single-phase quantities and warn
31 if (pp.contains("viscosity")) {
32 amrex::Print()
33 << "WARNING: single-phase viscosity has been specified but "
34 "will not be used! (TwoPhaseTransport)\n";
35 }
36 if (pp.contains("laminar_prandtl")) {
37 amrex::Print()
38 << "WARNING: single-phase laminar_prandtl has been specified "
39 "but will not be used! (TwoPhaseTransport)\n";
40 }
41
42 // Backwards compatibility
43 amrex::ParmParse pp_boussinesq_buoyancy("BoussinesqBuoyancy");
44 amrex::ParmParse pp_abl("ABL");
45 if (pp.contains("thermal_expansion_coefficient")) {
46 pp.get("thermal_expansion_coefficient", m_constant_beta);
47 if (pp_boussinesq_buoyancy.contains("thermal_expansion_coeff")) {
48 amrex::Print()
49 << "WARNING: BoussinesqBuoyancy.thermal_expansion_coeff "
50 "option has been deprecated in favor of "
51 "transport.thermal_expansion_coefficient. Ignoring the "
52 "BoussinesqBuoyancy option in favor of the transport "
53 "option."
54 << std::endl;
55 }
56 } else if (pp_boussinesq_buoyancy.contains("thermal_expansion_coeff")) {
57 amrex::Print()
58 << "WARNING: BoussinesqBuoyancy.thermal_expansion_coeff option "
59 "has been deprecated in favor of "
60 "transport.thermal_expansion_coefficient. Please replace "
61 "this option."
62 << std::endl;
63 pp_boussinesq_buoyancy.get(
64 "thermal_expansion_coeff", m_constant_beta);
65 }
66
67 if (pp.contains("reference_temperature")) {
68 pp.get("reference_temperature", m_reference_temperature);
69 if (pp_boussinesq_buoyancy.contains("reference_temperature")) {
70 amrex::Print()
71 << "WARNING: BoussinesqBuoyancy.reference_temperature "
72 "option has been deprecated in favor of "
73 "transport.reference_temperature. Ignoring the "
74 "BoussinesqBuoyancy option in favor of the transport "
75 "option."
76 << std::endl;
77 } else if (pp_abl.contains("reference_temperature")) {
78 amrex::Print()
79 << "WARNING: ABL.reference_temperature "
80 "option has been deprecated in favor of "
81 "transport.reference_temperature. Ignoring the "
82 "ABL option in favor of the transport "
83 "option."
84 << std::endl;
85 }
86 } else if (pp_boussinesq_buoyancy.contains("reference_temperature")) {
87 amrex::Print()
88 << "WARNING: BoussinesqBuoyancy.reference_temperature option "
89 "has been deprecated in favor of "
90 "transport.reference_temperature. Please replace "
91 "this option."
92 << std::endl;
93 pp_boussinesq_buoyancy.get(
94 "reference_temperature", m_reference_temperature);
95 } else if (pp_abl.contains("reference_temperature")) {
96 amrex::Print() << "WARNING: ABL.reference_temperature option "
97 "has been deprecated in favor of "
98 "transport.reference_temperature. Please replace "
99 "this option."
100 << std::endl;
101 pp_abl.get("reference_temperature", m_reference_temperature);
102 }
103 }
104
105 ~TwoPhaseTransport() override = default;
106
107 inline amrex::Real laminar_prandtl1() const { return m_Pr1; }
108 inline amrex::Real laminar_prandtl2() const { return m_Pr2; }
109
110 inline amrex::Real turbulent_prandtl() const { return m_Prt; }
111
112 static inline amrex::Real laminar_schmidt(const std::string& scalar_name)
113 {
114 amrex::ParmParse pp("transport");
115 const std::string key = scalar_name + "_laminar_schmidt";
116 amrex::Real lam_schmidt = 1.0;
117 pp.query(key.c_str(), lam_schmidt);
118 return lam_schmidt;
119 }
120
121 static inline amrex::Real turbulent_schmidt(const std::string& scalar_name)
122 {
123 amrex::ParmParse pp("transport");
124 const std::string key = scalar_name + "_turbulent_schmidt";
125 amrex::Real turb_schmidt = 1.0;
126 pp.query(key.c_str(), turb_schmidt);
127 return turb_schmidt;
128 }
129
131 inline std::unique_ptr<ScratchField> mu() override
132 {
133 // Select the interface capturing method
134 auto mu = m_repo.create_scratch_field(1, m_ngrow);
135
136 auto ifacetype = get_iface_method();
137 if (ifacetype == InterfaceCapturingMethod::VOF) {
138
139 const auto& vof = m_repo.get_field("vof");
140
141 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
142 for (amrex::MFIter mfi((*mu)(lev)); mfi.isValid(); ++mfi) {
143 const auto& vbx = mfi.growntilebox();
144 const auto& volfrac = vof(lev).const_array(mfi);
145 const auto& visc = (*mu)(lev).array(mfi);
146 const amrex::Real mu1 = m_mu1;
147 const amrex::Real mu2 = m_mu2;
148 amrex::ParallelFor(
149 vbx,
150 [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
151 visc(i, j, k) = mu1 * volfrac(i, j, k) +
152 mu2 * (1.0 - volfrac(i, j, k));
153 });
154 }
155 }
156
157 } else if (ifacetype == InterfaceCapturingMethod::LS) {
158
159 const auto& levelset = m_repo.get_field("levelset");
160 const auto& geom = m_repo.mesh().Geom();
161
162 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
163 for (amrex::MFIter mfi((*mu)(lev)); mfi.isValid(); ++mfi) {
164 const auto& vbx = mfi.growntilebox();
165 const auto& dx = geom[lev].CellSizeArray();
166 const auto& visc = (*mu)(lev).array(mfi);
167 const auto& phi = levelset(lev).const_array(mfi);
168 const amrex::Real eps =
169 std::cbrt(2. * dx[0] * dx[1] * dx[2]);
170 const amrex::Real mu1 = m_mu1;
171 const amrex::Real mu2 = m_mu2;
172 amrex::ParallelFor(
173 vbx,
174 [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
175 amrex::Real smooth_heaviside;
176 if (phi(i, j, k) > eps) {
177 smooth_heaviside = 1.0;
178 } else if (phi(i, j, k) < -eps) {
179 smooth_heaviside = 0.;
180 } else {
181 smooth_heaviside =
182 0.5 *
183 (1.0 + phi(i, j, k) / eps +
184 1.0 / M_PI *
185 std::sin(phi(i, j, k) * M_PI / eps));
186 }
187 visc(i, j, k) = mu1 * smooth_heaviside +
188 mu2 * (1.0 - smooth_heaviside);
189 });
190 }
191 }
192 }
193 return mu;
194 }
195
197 inline std::unique_ptr<ScratchField> alpha() override
198 {
199 // Select the interface capturing method
200 auto alpha = m_repo.create_scratch_field(1, m_ngrow);
201
202 auto ifacetype = get_iface_method();
203 if (ifacetype == InterfaceCapturingMethod::VOF) {
204
205 const auto& vof = m_repo.get_field("vof");
206
207 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
208 for (amrex::MFIter mfi((*alpha)(lev)); mfi.isValid(); ++mfi) {
209 const auto& vbx = mfi.growntilebox();
210 const auto& volfrac = vof(lev).const_array(mfi);
211 const auto& thdiff = (*alpha)(lev).array(mfi);
212 const amrex::Real mu1 = m_mu1;
213 const amrex::Real mu2 = m_mu2;
214 const amrex::Real Pr1 = m_Pr1;
215 const amrex::Real Pr2 = m_Pr2;
216 amrex::ParallelFor(
217 vbx,
218 [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
219 thdiff(i, j, k) =
220 mu1 / Pr1 * volfrac(i, j, k) +
221 mu2 / Pr2 * (1.0 - volfrac(i, j, k));
222 });
223 }
224 }
225
226 } else if (ifacetype == InterfaceCapturingMethod::LS) {
227
228 const auto& levelset = m_repo.get_field("levelset");
229 const auto& geom = m_repo.mesh().Geom();
230
231 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
232 for (amrex::MFIter mfi((*alpha)(lev)); mfi.isValid(); ++mfi) {
233 const auto& vbx = mfi.growntilebox();
234 const auto& dx = geom[lev].CellSizeArray();
235 const auto& visc = (*alpha)(lev).array(mfi);
236 const auto& phi = levelset(lev).const_array(mfi);
237 const amrex::Real eps =
238 std::cbrt(2. * dx[0] * dx[1] * dx[2]);
239 const amrex::Real mu1 = m_mu1;
240 const amrex::Real mu2 = m_mu2;
241 const amrex::Real Pr1 = m_Pr1;
242 const amrex::Real Pr2 = m_Pr2;
243 amrex::ParallelFor(
244 vbx,
245 [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
246 amrex::Real smooth_heaviside;
247 if (phi(i, j, k) > eps) {
248 smooth_heaviside = 1.0;
249 } else if (phi(i, j, k) < -eps) {
250 smooth_heaviside = 0.;
251 } else {
252 smooth_heaviside =
253 0.5 *
254 (1.0 + phi(i, j, k) / eps +
255 1.0 / M_PI *
256 std::sin(phi(i, j, k) * M_PI / eps));
257 }
258 visc(i, j, k) =
259 mu1 / Pr1 * smooth_heaviside +
260 mu2 / Pr2 * (1.0 - smooth_heaviside);
261 });
262 }
263 }
264 }
265 return alpha;
266 }
267
268 inline std::unique_ptr<ScratchField>
269 scalar_diffusivity(const std::string& scalar_name) override
270 {
271 amrex::Real lam_schmidt = laminar_schmidt(scalar_name);
272
273 amrex::Real inv_schmidt = 1.0 / lam_schmidt;
274 auto diff = mu();
275 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
276 (*diff)(lev).mult(inv_schmidt);
277 }
278
279 return diff;
280 }
281
283 inline std::unique_ptr<ScratchField> beta() const override
284 {
285 auto beta = m_repo.create_scratch_field(1, m_ngrow);
286 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
287#ifdef AMREX_USE_OMP
288#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
289#endif
290 for (amrex::MFIter mfi((*beta)(lev)); mfi.isValid(); ++mfi) {
291 const auto& bx = mfi.tilebox();
292 const auto& beta_arr = (*beta)(lev).array(mfi);
293 beta_impl(lev, mfi, bx, beta_arr);
294 }
295 }
296 return beta;
297 }
298
300 inline void beta_impl(
301 const int lev,
302 const amrex::MFIter& mfi,
303 const amrex::Box& bx,
304 const amrex::Array4<amrex::Real>& beta) const override
305 {
306
307 if (m_constant_beta > 0.0) {
308 const amrex::Real beta_val = m_constant_beta;
309 amrex::ParallelFor(
310 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
311 beta(i, j, k) = beta_val;
312 });
313 } else if (m_repo.field_exists("reference_temperature")) {
314 const auto& temp0 = m_repo.get_field("reference_temperature");
315 const auto& temp0_arr = temp0(lev).const_array(mfi);
316 amrex::ParallelFor(
317 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
318 beta(i, j, k) = 1.0 / temp0_arr(i, j, k);
319 });
320 } else {
321 const amrex::Real beta_val = 1.0 / m_reference_temperature;
322 amrex::ParallelFor(
323 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
324 beta(i, j, k) = beta_val;
325 });
326 }
327
328 auto ifacetype = get_iface_method();
329 if (ifacetype == InterfaceCapturingMethod::VOF) {
330 const auto& vof = m_repo.get_field("vof");
331 const auto& vof_arr = vof(lev).const_array(mfi);
332 amrex::ParallelFor(
333 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
334 if (vof_arr(i, j, k) > constants::TIGHT_TOL) {
335 beta(i, j, k) = 0.0;
336 }
337 });
338 } else if (ifacetype == InterfaceCapturingMethod::LS) {
339 const auto& levelset = m_repo.get_field("levelset");
340 const auto& dx = m_repo.mesh().Geom(lev).CellSizeArray();
341 const auto& phi_arr = levelset(lev).const_array(mfi);
342 const amrex::Real eps = std::cbrt(2. * dx[0] * dx[1] * dx[2]);
343 amrex::ParallelFor(
344 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
345 amrex::Real smooth_heaviside;
346 if (phi_arr(i, j, k) > eps) {
347 smooth_heaviside = 1.0;
348 } else if (phi_arr(i, j, k) < -eps) {
349 smooth_heaviside = 0.;
350 } else {
351 smooth_heaviside =
352 0.5 * (1.0 + phi_arr(i, j, k) / eps +
353 1.0 / M_PI *
354 std::sin(phi_arr(i, j, k) * M_PI / eps));
355 }
356 beta(i, j, k) *= (1 - smooth_heaviside);
357 });
358 }
359 }
360
361 inline amrex::Real reference_temperature() const override
362 {
364 }
365
367 inline std::unique_ptr<ScratchField> ref_theta() const override
368 {
369 if (m_reference_temperature < 0.0) {
370 amrex::Abort("Reference temperature was not set");
371 }
372
373 auto ref_theta = m_repo.create_scratch_field(1, m_ngrow);
374 for (int lev = 0; lev < m_repo.num_active_levels(); ++lev) {
375#ifdef AMREX_USE_OMP
376#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
377#endif
378 for (amrex::MFIter mfi((*ref_theta)(lev)); mfi.isValid(); ++mfi) {
379 const auto& bx = mfi.tilebox();
380 const auto& ref_theta_arr = (*ref_theta)(lev).array(mfi);
381 ref_theta_impl(lev, mfi, bx, ref_theta_arr);
382 }
383 }
384 return ref_theta;
385 }
386
388 inline void ref_theta_impl(
389 const int lev,
390 const amrex::MFIter& mfi,
391 const amrex::Box& bx,
392 const amrex::Array4<amrex::Real>& ref_theta) const override
393 {
394 if (m_reference_temperature < 0.0) {
395 amrex::Abort("Reference temperature was not set");
396 }
397
398 if (m_repo.field_exists("reference_temperature")) {
399 auto& temp0 = m_repo.get_field("reference_temperature");
400 const auto& temp0_arr = temp0(lev).const_array(mfi);
401 amrex::ParallelFor(
402 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
403 ref_theta(i, j, k) = temp0_arr(i, j, k);
404 });
405 } else {
406 const amrex::Real ref_theta_val = m_reference_temperature;
407 amrex::ParallelFor(
408 bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
409 ref_theta(i, j, k) = ref_theta_val;
410 });
411 }
412 }
413
414private:
417
420
422 amrex::Real m_mu1{1.0e-3};
423
425 amrex::Real m_mu2{1.0e-5};
426
428 amrex::Real m_Pr1{7.2};
429
431 amrex::Real m_Pr2{0.7};
432
434 amrex::Real m_Prt{1.0};
435
437 amrex::Real m_constant_beta{0.0};
438
440 amrex::Real m_reference_temperature{-1.0};
441
443 {
444 if (!m_physics_mgr.contains("MultiPhase")) {
445 amrex::Abort("TwoPhaseTransport requires MultiPhase physics");
446 }
447 const auto& multiphase = m_physics_mgr.get<MultiPhase>();
448 return multiphase.interface_capturing_method();
449 }
450};
451
452} // namespace amr_wind::transport
453
454#endif /* TWOPHASETRANSPORT_H */
Definition CFDSim.H:54
bool contains(const std::string &key) const
Query if an object exists using the lookup key.
Definition CollMgr.H:55
Definition FieldRepo.H:86
std::unique_ptr< ScratchField > create_scratch_field(const std::string &name, const int ncomp=1, const int nghost=0, const FieldLoc floc=FieldLoc::CELL) const
Definition FieldRepo.cpp:302
Field & get_field(const std::string &name, const FieldState fstate=FieldState::New) const
Definition FieldRepo.cpp:149
int num_active_levels() const noexcept
Total number of levels currently active in the AMR mesh.
Definition FieldRepo.H:361
bool field_exists(const std::string &name, const FieldState fstate=FieldState::New) const
Definition FieldRepo.cpp:215
const amrex::AmrCore & mesh() const
Return a reference to the underlying AMR mesh instance.
Definition FieldRepo.H:358
Definition MultiPhase.H:27
InterfaceCapturingMethod interface_capturing_method() const
Definition MultiPhase.cpp:80
Definition Physics.H:100
T & get()
Return a concrete physics instance.
Definition Physics.H:104
Definition TwoPhaseTransport.H:14
amrex::Real laminar_prandtl1() const
Definition TwoPhaseTransport.H:107
std::unique_ptr< ScratchField > ref_theta() const override
Return the reference temperature.
Definition TwoPhaseTransport.H:367
amrex::Real reference_temperature() const override
Definition TwoPhaseTransport.H:361
amrex::Real m_Prt
Turbulent Prandtl number.
Definition TwoPhaseTransport.H:434
std::unique_ptr< ScratchField > beta() const override
Return the thermal expansion coefficient.
Definition TwoPhaseTransport.H:283
std::unique_ptr< ScratchField > mu() override
Return the dynamic visocity field.
Definition TwoPhaseTransport.H:131
std::unique_ptr< ScratchField > scalar_diffusivity(const std::string &scalar_name) override
Definition TwoPhaseTransport.H:269
void ref_theta_impl(const int lev, const amrex::MFIter &mfi, const amrex::Box &bx, const amrex::Array4< amrex::Real > &ref_theta) const override
Compute the reference temperature.
Definition TwoPhaseTransport.H:388
amrex::Real turbulent_prandtl() const
Definition TwoPhaseTransport.H:110
InterfaceCapturingMethod get_iface_method() const
Definition TwoPhaseTransport.H:442
amrex::Real m_reference_temperature
Reference temperature.
Definition TwoPhaseTransport.H:440
amrex::Real m_Pr1
Phase 1 (liquid) Prandtl number.
Definition TwoPhaseTransport.H:428
void beta_impl(const int lev, const amrex::MFIter &mfi, const amrex::Box &bx, const amrex::Array4< amrex::Real > &beta) const override
Compute the thermal expansion coefficient.
Definition TwoPhaseTransport.H:300
static constexpr bool constant_properties
Definition TwoPhaseTransport.H:16
TwoPhaseTransport(const CFDSim &sim)
Definition TwoPhaseTransport.H:20
amrex::Real m_constant_beta
Constant thermal expansion coefficient.
Definition TwoPhaseTransport.H:437
amrex::Real laminar_prandtl2() const
Definition TwoPhaseTransport.H:108
amrex::Real m_mu1
Phase 1 (liquid) dynamic molecular viscosity.
Definition TwoPhaseTransport.H:422
FieldRepo & m_repo
Reference to the field repository (for creating scratch fields)
Definition TwoPhaseTransport.H:416
static std::string identifier()
Definition TwoPhaseTransport.H:18
amrex::Real m_mu2
Phase 2 (gas) dynamic molecular viscosity.
Definition TwoPhaseTransport.H:425
static amrex::Real turbulent_schmidt(const std::string &scalar_name)
Definition TwoPhaseTransport.H:121
amrex::Real m_Pr2
Phase 2 (gas) Prandtl number.
Definition TwoPhaseTransport.H:431
static amrex::Real laminar_schmidt(const std::string &scalar_name)
Definition TwoPhaseTransport.H:112
const PhysicsMgr & m_physics_mgr
Reference to the physics manager.
Definition TwoPhaseTransport.H:419
std::unique_ptr< ScratchField > alpha() override
Return the thermal diffusivity field (later divided by density, though)
Definition TwoPhaseTransport.H:197
static constexpr amrex::Real TIGHT_TOL
A tight tolerance.
Definition constants.H:19
Definition CFDSim.H:26
InterfaceCapturingMethod
Definition MultiPhase.H:21