/home/runner/work/amr-wind/amr-wind/amr-wind/ocean_waves/relaxation_zones/waves2amr_ops.H Source File

AMR-Wind API: /home/runner/work/amr-wind/amr-wind/amr-wind/ocean_waves/relaxation_zones/waves2amr_ops.H Source File
AMR-Wind API v0.1.0
CFD solver for wind plant simulations
waves2amr_ops.H
Go to the documentation of this file.
1#ifndef W2A_WAVES_OPS_H
2#define W2A_WAVES_OPS_H
3
13#include "AMReX_MultiFabUtil.H"
14
15#ifdef AMR_WIND_USE_W2A
16namespace {
17int evaluate_read_resize(
18 int& ntime,
19 bool& read_flag,
20 bool& resize_flag,
21 amrex::Real& wtime,
22 amrex::Real& t_last,
23 const int new_ntime,
24 const amrex::Real wtinit,
25 const amrex::Real wdt,
26 const amrex::Real time)
27{
28 // Flag to indicate that data must be read twice for interpolation
29 int double_data = 0;
30 // Check if time indicates reading must take place
31 if (new_ntime != ntime) {
32 // New data is needed - reading should happen
33 read_flag = true;
34 // If time index has proceeded more than 1 step
35 if (new_ntime > ntime + 1) {
36 // Double reading is necessary
37 double_data = 1;
38 }
39 // Time index for reading
40 ntime = new_ntime;
41 // Sim time to go with recorded data
42 wtime = new_ntime * wdt + wtinit;
43 // If double reading is deemed necessary, check for convenience
44 if (double_data == 1 && std::abs(wtime - time) <= 1e-10) {
45 // Reading can be done just once, w2a fields replace ow fields
46 double_data = 2;
47 }
48 }
49 // Check if reading must take place for other reasons
50 if (t_last < -1e-10) {
51 // Signifies initialization from scratch without waves or a restart
52 read_flag = true;
53 // Resizing needs to happen for the first time
54 resize_flag = true;
55
56 // Confirm that new time is not coincident with modes time step
57 if (std::abs(wtime - time) > 1e-10) {
58 // Data must be read before and after wtime
59 double_data = 1;
60 } else {
61 // Data need only be read once, w2a fields will replace ow fields
62 double_data = 2;
63 }
64
65 } else if (std::abs(t_last) < 1e-10) {
66 // Signifies initialization with waves
67 read_flag = true;
68 // Resizing needs to happen for the first time
69 resize_flag = true;
70
71 // levelset and velocity fields are up-to-date at t=0
72 // interpolation is ready to go
73 }
74 // Record latest time as 'last' for next timestep
75 t_last = time;
76 // Return flag regarding double reading
77 return double_data;
78}
79
80void postprocess_velocity_mfab_liquid(
81 amrex::MultiFab& vel_mfab,
82 amrex::MultiFab& lvs_mfab,
83 const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> dx)
84{
85 auto vel = vel_mfab.arrays();
86 const auto phi = lvs_mfab.const_arrays();
87 amrex::ParallelFor(
88 vel_mfab, amrex::IntVect(3),
89 [=] AMREX_GPU_DEVICE(int nbx, int i, int j, int k) noexcept {
90 // Set velocity to zero if no liquid present
91 const amrex::Real cell_length_2D =
92 std::sqrt(dx[0] * dx[0] + dx[2] * dx[2]);
93 if (phi[nbx](i, j, k) + cell_length_2D < 0) {
94 vel[nbx](i, j, k, 0) = 0.0;
95 vel[nbx](i, j, k, 1) = 0.0;
96 vel[nbx](i, j, k, 2) = 0.0;
97 }
98 });
99 amrex::Gpu::streamSynchronize();
100}
101
102void postprocess_velocity_field_liquid(
103 amr_wind::Field& vel_field,
104 amr_wind::Field& lvs_field,
105 amrex::Vector<amrex::Geometry>& geom_all)
106{
107 int nlevels = vel_field.repo().num_active_levels();
108 for (int lev = 0; lev < nlevels; ++lev) {
109 const auto& dx_lev = geom_all[lev].CellSizeArray();
110 postprocess_velocity_mfab_liquid(
111 vel_field(lev), lvs_field(lev), dx_lev);
112 }
113}
114
115int update_offset_timestep(const int ntime, const int n0)
116{
117 // Offending timestep (goes too far): ntime + offset
118 // Farthest back timesteps are permitted to go: n0
119 // Subtract offset by offending timestep, add back lower limit
120 // new offset = offset - (ntime + offset) + n0
121 return (-ntime + n0);
122}
123
124void populate_fields_all_levels(
126 amrex::Vector<amrex::Geometry>& geom_all,
127 amr_wind::Field& lvs_field,
128 amr_wind::Field& vel_field,
129 int ntime_off = 0)
130{
131
132 // Get data from modes
133 bool no_EOF =
134 wdata.is_ocean
135 ? wdata.c_rmodes.get_data(
136 wdata.ntime + wdata.n_offset + ntime_off, wdata.c_mX,
137 wdata.c_mY, wdata.c_mZ, wdata.c_mFS)
138 : wdata.r_rmodes.get_data(
139 wdata.ntime + wdata.n_offset + ntime_off, wdata.r_mX,
140 wdata.r_mY, wdata.r_mZ, wdata.r_mFS, wdata.r_mAdd);
141 // Navigate when end of file is reached
142 if (!no_EOF) {
143 // End of file detected, reset reading
144 wdata.n_offset = update_offset_timestep(wdata.ntime, wdata.n_winit);
145 // Print warning to screen
146 amrex::Print() << "WARNING (waves2amr_ops): end of mode data file "
147 "detected, resetting to beginning of mode data.\n";
148 // Read data again, now from a valid timestep
149 no_EOF = wdata.is_ocean
150 ? wdata.c_rmodes.get_data(
151 wdata.ntime + wdata.n_offset + ntime_off, wdata.c_mX,
152 wdata.c_mY, wdata.c_mZ, wdata.c_mFS)
153 : wdata.r_rmodes.get_data(
154 wdata.ntime + wdata.n_offset + ntime_off, wdata.r_mX,
155 wdata.r_mY, wdata.r_mZ, wdata.r_mFS, wdata.r_mAdd);
156 // If no valid data is detected at this point, abort
157 if (!no_EOF) {
158 amrex::Abort(
159 "waves2amr_ops: end of mode data file detected after "
160 "resetting to beginning; please evaluate HOS_init_time "
161 "or HOS_init_timestep and check the length of the mode "
162 "file.");
163 }
164 }
165
166 // Convert to spatial data in vectors
167 if (wdata.is_ocean) {
168 modes_hosgrid::copy_complex(
169 wdata.n0, wdata.n1, wdata.c_mFS, wdata.c_eta_mptr);
170 modes_hosgrid::populate_hos_eta(
171 wdata.c_rmodes, wdata.plan_vector, wdata.c_eta_mptr,
172 wdata.sp_eta_vec);
173 } else {
174 modes_hosgrid::copy_real(
175 wdata.n0, wdata.n1, wdata.r_mFS, wdata.r_eta_mptr);
176 modes_hosgrid::populate_hos_eta(
177 wdata.r_rmodes, wdata.plan_vector, wdata.r_eta_mptr,
178 wdata.sp_eta_vec);
179 }
180
181 for (int iht = 0; iht < wdata.indvec.size(); ++iht) {
182 // Get sample height
183 amrex::Real ht = wdata.hvec[wdata.indvec[iht]];
184 // Sample velocity
185 if (wdata.is_ocean) {
186 modes_hosgrid::populate_hos_vel(
187 wdata.c_rmodes, ht, wdata.zsl, wdata.c_mX, wdata.c_mY,
188 wdata.c_mZ, wdata.plan_vector, wdata.c_u_mptr, wdata.c_v_mptr,
189 wdata.c_w_mptr, wdata.sp_u_vec, wdata.sp_v_vec, wdata.sp_w_vec,
190 iht * wdata.n0 * wdata.n1);
191 } else {
192 modes_hosgrid::populate_hos_vel(
193 wdata.r_rmodes, ht, wdata.zsl, wdata.r_mX, wdata.r_mY,
194 wdata.r_mZ, wdata.r_mAdd, wdata.plan_vector, wdata.r_u_mptr,
195 wdata.r_v_mptr, wdata.r_w_mptr, wdata.au_mptr, wdata.av_mptr,
196 wdata.aw_mptr, wdata.sp_u_vec, wdata.sp_v_vec, wdata.sp_w_vec,
197 iht * wdata.n0 * wdata.n1);
198 }
199 }
200
201 // Interpolate to fields (vector of MultiFabs)
202 interp_to_mfab::interp_eta_to_levelset_field(
203 wdata.n0_sp, wdata.n1_sp, wdata.dx0, wdata.dx1, wdata.xlo0, wdata.xlo1,
204 wdata.zsl, wdata.is_ocean, wdata.sp_eta_vec, lvs_field.vec_ptrs(),
205 geom_all);
206 interp_to_mfab::interp_velocity_to_field(
207 wdata.n0_sp, wdata.n1_sp, wdata.dx0, wdata.dx1, wdata.xlo0, wdata.xlo1,
208 wdata.is_ocean, wdata.indvec, wdata.hvec, wdata.sp_u_vec,
209 wdata.sp_v_vec, wdata.sp_w_vec, vel_field.vec_ptrs(), geom_all);
210
211 // Zero velocity in pure gas cells
212 postprocess_velocity_field_liquid(vel_field, lvs_field, geom_all);
213}
214
215} // namespace
216#endif
217
219
220template <>
222{
224 // cppcheck-suppress constParameterReference
225 W2AWaves::DataType& data,
226 const ::amr_wind::utils::MultiParser& pp)
227 {
228// Check for W2A initialization
229#ifndef AMR_WIND_USE_W2A
230 // Assert Waves2AMR must be used for initial condition file
231 amrex::Abort(
232 "ocean_waves/W2AWaves: AMR-Wind was not built with Waves2AMR "
233 "support; associated wave data cannot be processed for relaxation "
234 "zones.");
235
236 amrex::ignore_unused(data, pp);
237#else
238 auto& wdata = data.meta();
239 auto& info = data.info();
240 relaxation_zones::read_inputs(wdata, info, pp);
241
242 if (wdata.current > constants::TIGHT_TOL) {
243 amrex::Abort(
244 "Current is specified as nonzero, but current is not yet "
245 "implemented for W2A Waves.");
246 }
247
248 pp.get("HOS_modes_filename", wdata.modes_file);
249 pp.query("HOS_simulation_is_ocean", wdata.is_ocean);
250 pp.query("HOS_init_timestep", wdata.ntime);
251 if (!pp.contains("HOS_init_timestep")) {
252 pp.query("HOS_init_time", wdata.t_winit);
253 }
254 // Check effect !!!
255 pp.query("HOS_domain_offset_x", wdata.xlo0);
256 pp.query("HOS_domain_offset_y", wdata.xlo1);
257
258 // Default fftw_plan is deterministic
259 std::string fftw_planner_flag{"estimate"};
260 pp.query("fftw_planner_flag", fftw_planner_flag);
261
262 amrex::Vector<amrex::Real> prob_lo_input(AMREX_SPACEDIM);
263 amrex::ParmParse pp_geom("geometry");
264 pp_geom.getarr("prob_lo", prob_lo_input);
265
266 // Read user inputs to height vector
267 int nheights = 0;
268 int nh_above = 1;
269 amrex::Real dz0 = 0.;
270 pp.get("number_interp_points_in_z", nheights);
271 pp.get("interp_spacing_at_surface", dz0);
272 pp.query("number_interp_above_surface", nh_above);
273
274 // Initialize mode reader
275 bool file_exists{false};
276 if (wdata.is_ocean) {
277 file_exists = wdata.c_rmodes.initialize(wdata.modes_file, true);
278 } else {
279 file_exists = wdata.r_rmodes.initialize(wdata.modes_file, false);
280 }
281
282 // Abort if file does not exist
283 if (!file_exists) {
284 amrex::Abort(
285 "Waves2AMR ReadInputsOp: modes file requested does not exist");
286 }
287 // modify initialize to differentiate between file not existing and file
288 // being wrong type (ocean or NWT)
289
290 // Get dt of HOS data
291 wdata.dt_modes = wdata.is_ocean ? wdata.c_rmodes.get_dtout()
292 : wdata.r_rmodes.get_dtout();
293
294 // Get initial time and timestep synced
295 if (wdata.t_winit > 0.0) {
296 // If initial time was specified
297 // Get time index near requested time
298 wdata.ntime =
299 wdata.is_ocean
300 ? wdata.c_rmodes.time2step(wdata.t_winit, wdata.ntime)
301 : wdata.r_rmodes.time2step(wdata.t_winit, wdata.ntime);
302 ;
303 // Sync time to time index
304 wdata.t_winit = wdata.dt_modes * wdata.ntime;
305 // Save first timestep
306 wdata.n_winit = wdata.ntime;
307 } else {
308 // If initial timestep is being used
309 wdata.t_winit = wdata.dt_modes * wdata.ntime;
310 // Save first timestep
311 wdata.n_winit = wdata.ntime;
312 }
313
314 // Initialize variables to store modes
315 amrex::Real depth{0.0};
316 if (wdata.is_ocean) {
317 int vsize = wdata.c_rmodes.get_vector_size();
318 double initval = 0.0;
319 wdata.c_mX.resize(vsize, initval);
320 wdata.c_mY.resize(vsize, initval);
321 wdata.c_mZ.resize(vsize, initval);
322 wdata.c_mFS.resize(vsize, initval);
323
324 // Get dimensions of data
325 wdata.n0 = wdata.c_rmodes.get_first_fft_dimension();
326 wdata.n1 = wdata.c_rmodes.get_second_fft_dimension();
327 wdata.n0_sp = wdata.c_rmodes.get_first_spatial_dimension();
328 wdata.n1_sp = wdata.c_rmodes.get_second_spatial_dimension();
329 // Get resolution
330 wdata.dx0 = wdata.c_rmodes.get_xlen() / wdata.n0_sp;
331 wdata.dx1 = wdata.c_rmodes.get_ylen() / wdata.n1_sp;
332 // Get depth
333 depth = wdata.c_rmodes.get_depth();
334 // Get dimensional length
335 wdata.dimL = wdata.c_rmodes.get_L();
336 // Get nominal last timestep of data
337 wdata.n_wstop =
338 (int)((wdata.c_rmodes.get_Tstop() + 1e-8) / wdata.dt_modes);
339 } else {
340 int vsize = wdata.r_rmodes.get_vector_size();
341 int vasize = wdata.r_rmodes.get_addl_vector_size();
342 double initval = 0.0;
343 wdata.r_mX.resize(vsize, initval);
344 wdata.r_mY.resize(vsize, initval);
345 wdata.r_mZ.resize(vsize, initval);
346 wdata.r_mFS.resize(vsize, initval);
347 wdata.r_mAdd.resize(vasize, initval);
348
349 // Get dimensions of data
350 wdata.n0 = wdata.r_rmodes.get_first_fft_dimension();
351 wdata.n1 = wdata.r_rmodes.get_second_fft_dimension();
352 wdata.n2 = wdata.r_rmodes.get_third_dimension();
353 wdata.n0_sp = wdata.r_rmodes.get_first_spatial_dimension();
354 wdata.n1_sp = wdata.r_rmodes.get_second_spatial_dimension();
355 // Get resolution (slightly different for NWT)
356 wdata.dx0 = wdata.r_rmodes.get_xlen() / (wdata.n0_sp - 1);
357 wdata.dx1 = wdata.r_rmodes.get_ylen() / (wdata.n1_sp - 1);
358 // Get depth
359 depth = wdata.r_rmodes.get_depth();
360 // Get dimensional length
361 wdata.dimL = wdata.r_rmodes.get_L();
362 // Get nominal last timestep of data
363 wdata.n_wstop =
364 (int)((wdata.r_rmodes.get_Tstop() + 1e-8) / wdata.dt_modes);
365 }
366
367 // Check if stop time is exceeded, introduce offset to ntime
368 if (wdata.ntime + wdata.n_offset > wdata.n_wstop) {
369 // If exceeding stop step, calculate new offset
370 wdata.n_offset = update_offset_timestep(wdata.ntime, wdata.n_winit);
371 // Print warning to screen
372 amrex::Print()
373 << "WARNING (waves2amr_ops): available mode data exceeded, "
374 "resetting to beginning of mode data.\n";
375 }
376
377 // Warning if depth does not correspond to simulation
378 if (std::abs(depth - (wdata.zsl - prob_lo_input[2])) > 1e-3 * depth) {
379 amrex::Print()
380 << "WARNING: Mismatch between water depths from AMR-Wind "
381 "domain and HOS data interpreted by Waves2AMR. \n ^This "
382 "warning is not a concern when using waves as terrain.\n";
383 }
384
385 // Allocate pointers for FFTW
386 if (wdata.is_ocean) {
387 wdata.c_eta_mptr =
388 modes_hosgrid::allocate_complex(wdata.n0, wdata.n1);
389 wdata.c_u_mptr =
390 modes_hosgrid::allocate_complex(wdata.n0, wdata.n1);
391 wdata.c_v_mptr =
392 modes_hosgrid::allocate_complex(wdata.n0, wdata.n1);
393 wdata.c_w_mptr =
394 modes_hosgrid::allocate_complex(wdata.n0, wdata.n1);
395 } else {
396 wdata.r_eta_mptr = modes_hosgrid::allocate_real(wdata.n0, wdata.n1);
397 wdata.r_u_mptr = modes_hosgrid::allocate_real(wdata.n0, wdata.n1);
398 wdata.r_v_mptr = modes_hosgrid::allocate_real(wdata.n0, wdata.n1);
399 wdata.r_w_mptr = modes_hosgrid::allocate_real(wdata.n0, wdata.n1);
400 wdata.au_mptr = modes_hosgrid::allocate_real(wdata.n0, 1);
401 wdata.av_mptr = modes_hosgrid::allocate_real(wdata.n0, 1);
402 wdata.aw_mptr = modes_hosgrid::allocate_real(wdata.n0, 1);
403 }
404
405 // Set up planner flag based on input
406 auto plan_f = modes_hosgrid::planner_flags::estimate;
407 if (fftw_planner_flag == "patient") {
408 plan_f = modes_hosgrid::planner_flags::patient;
409 } else if (fftw_planner_flag == "exhaustive") {
410 plan_f = modes_hosgrid::planner_flags::exhaustive;
411 } else if (fftw_planner_flag == "measure") {
412 plan_f = modes_hosgrid::planner_flags::measure;
413 } else if (!(fftw_planner_flag == "estimate")) {
414 amrex::Print()
415 << "WARNING (waves2amr_ops): invalid fftw_planner_flag "
416 "specified; defaulting to estimate (FFTW_ESTIMATE).\n";
417 }
418 // Set up plan for FFTW
419 if (wdata.is_ocean) {
420 wdata.plan_vector.emplace_back(modes_hosgrid::plan_ifftw(
421 wdata.n0, wdata.n1, wdata.c_eta_mptr, plan_f));
422 } else {
423 modes_hosgrid::plan_ifftw_nwt(
424 wdata.n0, wdata.n1, wdata.plan_vector, wdata.r_eta_mptr,
425 plan_f);
426 }
427
428 // Create height vector for velocity mode conversion before
429 // interpolation, with prob_lo as bottom
430 int flag = interp_to_mfab::create_height_vector(
431 wdata.hvec, nheights, dz0, wdata.zsl, prob_lo_input[2], nh_above);
432 // Fail if flag indicates it should
433 if (flag > 0) {
434 amrex::Abort(
435 "Waves2AMR ReadInputsOp: create_height_vector error, failure "
436 "code " +
437 std::to_string(flag));
438 }
439
440 // Always get modes on every processor for initialization
441 bool no_EOF =
442 wdata.is_ocean
443 ? wdata.c_rmodes.get_data(
444 wdata.ntime + wdata.n_offset, wdata.c_mX, wdata.c_mY,
445 wdata.c_mZ, wdata.c_mFS)
446 : wdata.r_rmodes.get_data(
447 wdata.ntime + wdata.n_offset, wdata.r_mX, wdata.r_mY,
448 wdata.r_mZ, wdata.r_mFS, wdata.r_mAdd);
449 if (!no_EOF) {
450 // End of file detected, reset reading
451 wdata.n_offset = update_offset_timestep(wdata.ntime, wdata.n_winit);
452 // Print warning to screen
453 amrex::Print()
454 << "WARNING (waves2amr_ops): end of mode data file "
455 "detected, resetting to beginning of mode data.\n";
456 // Read data again, now from a valid timestep
457 no_EOF =
458 wdata.is_ocean
459 ? wdata.c_rmodes.get_data(
460 wdata.ntime + wdata.n_offset, wdata.c_mX, wdata.c_mY,
461 wdata.c_mZ, wdata.c_mFS)
462 : wdata.r_rmodes.get_data(
463 wdata.ntime + wdata.n_offset, wdata.r_mX, wdata.r_mY,
464 wdata.r_mZ, wdata.r_mFS, wdata.r_mAdd);
465 // If no valid data is detected at this point, abort
466 if (!no_EOF) {
467 amrex::Abort(
468 "waves2amr_ops: end of mode data file detected after "
469 "resetting to beginning; please evaluate HOS_init_time "
470 "or HOS_init_timestep and check the length of the mode "
471 "file.");
472 }
473 }
474
475 // Convert modes to spatial data
476 if (wdata.is_ocean) {
477 modes_hosgrid::copy_complex(
478 wdata.n0, wdata.n1, wdata.c_mFS, wdata.c_eta_mptr);
479 wdata.sp_eta_vec.resize(
480 static_cast<size_t>(wdata.n0) * static_cast<size_t>(wdata.n1),
481 0.0);
482 modes_hosgrid::populate_hos_eta(
483 wdata.c_rmodes, wdata.plan_vector, wdata.c_eta_mptr,
484 wdata.sp_eta_vec);
485 // Mesh is not yet created, so get data at every height
486 const auto n_hts = wdata.hvec.size();
487 wdata.sp_u_vec.resize(
488 static_cast<size_t>(wdata.n0 * wdata.n1) * n_hts);
489 wdata.sp_v_vec.resize(
490 static_cast<size_t>(wdata.n0 * wdata.n1) * n_hts);
491 wdata.sp_w_vec.resize(
492 static_cast<size_t>(wdata.n0 * wdata.n1) * n_hts);
493 for (int iht = 0; iht < static_cast<int>(n_hts); ++iht) {
494 // Get sample height
495 amrex::Real ht = wdata.hvec[iht];
496 // Sample velocity
497 modes_hosgrid::populate_hos_vel(
498 wdata.c_rmodes, ht, wdata.zsl, wdata.c_mX, wdata.c_mY,
499 wdata.c_mZ, wdata.plan_vector, wdata.c_u_mptr,
500 wdata.c_v_mptr, wdata.c_w_mptr, wdata.sp_u_vec,
501 wdata.sp_v_vec, wdata.sp_w_vec, iht * wdata.n0 * wdata.n1);
502 }
503 } else {
504 modes_hosgrid::copy_real(
505 wdata.n0, wdata.n1, wdata.r_mFS, wdata.r_eta_mptr);
506 wdata.sp_eta_vec.resize(
507 static_cast<size_t>(wdata.n0) * static_cast<size_t>(wdata.n1),
508 0.0);
509 modes_hosgrid::populate_hos_eta(
510 wdata.r_rmodes, wdata.plan_vector, wdata.r_eta_mptr,
511 wdata.sp_eta_vec);
512 // Mesh is not yet created, so get data at every height
513 const auto n_hts = wdata.hvec.size();
514 wdata.sp_u_vec.resize(
515 static_cast<size_t>(wdata.n0 * wdata.n1) * n_hts);
516 wdata.sp_v_vec.resize(
517 static_cast<size_t>(wdata.n0 * wdata.n1) * n_hts);
518 wdata.sp_w_vec.resize(
519 static_cast<size_t>(wdata.n0 * wdata.n1) * n_hts);
520 for (int iht = 0; iht < static_cast<int>(n_hts); ++iht) {
521 // Get sample height
522 amrex::Real ht = wdata.hvec[iht];
523 // Sample velocity
524 modes_hosgrid::populate_hos_vel(
525 wdata.r_rmodes, ht, wdata.zsl, wdata.r_mX, wdata.r_mY,
526 wdata.r_mZ, wdata.r_mAdd, wdata.plan_vector, wdata.r_u_mptr,
527 wdata.r_v_mptr, wdata.r_w_mptr, wdata.au_mptr,
528 wdata.av_mptr, wdata.aw_mptr, wdata.sp_u_vec,
529 wdata.sp_v_vec, wdata.sp_w_vec, iht * wdata.n0 * wdata.n1);
530 }
531 }
532
533 // Declare fields for HOS
534 auto& w2a_levelset =
535 data.sim().repo().declare_field("w2a_levelset", 1, 3, 1);
536 auto& w2a_velocity = data.sim().repo().declare_field(
537 "w2a_velocity", AMREX_SPACEDIM, 3, 1);
538
539 // Extrapolation can work well when finer data is available
540 w2a_levelset.set_default_fillpatch_bc(data.sim().time());
541 w2a_velocity.set_default_fillpatch_bc(data.sim().time());
542#endif
543 }
544}; // namespace ops
545
546template <>
548{
549 void
550 // cppcheck-suppress constParameterReference
553 data, int level, const amrex::Geometry & geom, bool multiphase_mode)
554 {
555
556#ifdef AMR_WIND_USE_W2A
557 auto& wdata = data.meta();
558
559 auto& sim = data.sim();
560
561 // Fill ow fields, then populate flow fields according to setup
562 auto& ow_levelset = sim.repo().get_field("ow_levelset");
563 auto& ow_velocity = sim.repo().get_field("ow_velocity");
564
565 auto& velocity = sim.repo().get_field("velocity");
566 // Set w2a fields to default values to prep for updates
567 auto& w2a_levelset = sim.repo().get_field("w2a_levelset");
568 auto& w2a_velocity = sim.repo().get_field("w2a_velocity");
569
570 Field* levelset{nullptr};
571 if (multiphase_mode) {
572 levelset = &sim.repo().get_field("levelset");
573 }
574
575 const auto& problo = geom.ProbLoArray();
576 const auto& probhi = geom.ProbHiArray();
577 const auto& dx = geom.CellSizeArray();
578
579 // Set t_last to 0.0 to signify information read in
580 wdata.t_last = 0.0;
581
582 // indvec is complete upon initialization (all heights every proc)
583 amrex::Vector<int> indvec(wdata.hvec.size());
584 std::iota(indvec.begin(), indvec.end(), 0);
585 // Interpolate to MultiFabs (one level at a time)
586 interp_to_mfab::interp_eta_to_levelset_multifab(
587 wdata.n0_sp, wdata.n1_sp, wdata.dx0, wdata.dx1, wdata.xlo0,
588 wdata.xlo1, wdata.zsl, wdata.is_ocean, wdata.sp_eta_vec,
589 ow_levelset(level), problo, dx);
590 interp_to_mfab::interp_velocity_to_multifab(
591 wdata.n0_sp, wdata.n1_sp, wdata.dx0, wdata.dx1, wdata.xlo0,
592 wdata.xlo1, wdata.is_ocean, indvec, wdata.hvec, wdata.sp_u_vec,
593 wdata.sp_v_vec, wdata.sp_w_vec, ow_velocity(level), problo, dx);
594 // Zero velocity in pure air cells
595 postprocess_velocity_mfab_liquid(
596 ow_velocity(level), ow_levelset(level), dx);
597
598 // Populate flow fields according to intended forcing and init setup
599 const auto& ow_phi = ow_levelset(level).const_arrays();
600 const auto& ow_vel = ow_velocity(level).const_arrays();
601 const auto& vel = velocity(level).arrays();
602 const auto& phi_arrs = multiphase_mode
603 ? (*levelset)(level).arrays()
604 : amrex::MultiArray4<amrex::Real>();
605
606 const auto& w2a_phi = w2a_levelset(level).arrays();
607 const auto& w2a_vel = w2a_velocity(level).arrays();
608
609 const amrex::Real gen_length = wdata.gen_length;
610 const amrex::Real beach_length = wdata.beach_length;
611 const amrex::Real zero_sea_level = wdata.zsl;
612
613 const bool has_beach = wdata.has_beach && multiphase_mode;
614 const bool init_wave_field = wdata.init_wave_field || !multiphase_mode;
615
616 amrex::ParallelFor(
617 velocity(level), amrex::IntVect(3),
618 [=] AMREX_GPU_DEVICE(int nbx, int i, int j, int k) noexcept {
619 const amrex::Real x = problo[0] + (i + 0.5) * dx[0];
620 const amrex::Real z = problo[2] + (k + 0.5) * dx[2];
621
622 // Wave profile
623 const utils::WaveVec wave_sol{
624 ow_vel[nbx](i, j, k, 0), ow_vel[nbx](i, j, k, 1),
625 ow_vel[nbx](i, j, k, 2), ow_phi[nbx](i, j, k) + z};
626 // Quiescent profile
627 const utils::WaveVec quiescent{0.0, 0.0, 0.0, zero_sea_level};
628
629 // Specify initial state for each region of domain
630 const auto bulk = init_wave_field ? wave_sol : quiescent;
631 const auto outlet = has_beach ? quiescent : wave_sol;
632
633 const auto local_profile = utils::harmonize_profiles_1d(
634 x, problo[0], gen_length, probhi[0], beach_length, wave_sol,
635 bulk, outlet);
636
637 const amrex::Real phi = local_profile[3] - z;
638 const amrex::Real cell_length_2D =
639 std::sqrt(dx[0] * dx[0] + dx[2] * dx[2]);
640 if (phi + cell_length_2D >= 0) {
641 vel[nbx](i, j, k, 0) = local_profile[0];
642 vel[nbx](i, j, k, 1) = local_profile[1];
643 vel[nbx](i, j, k, 2) = local_profile[2];
644 }
645
646 if (multiphase_mode) {
647 phi_arrs[nbx](i, j, k) = phi;
648 }
649
650 // Default w2a values matter for where no updates happen
651 w2a_phi[nbx](i, j, k) = quiescent[3] - z;
652 w2a_vel[nbx](i, j, k, 0) = quiescent[0];
653 w2a_vel[nbx](i, j, k, 1) = quiescent[0];
654 w2a_vel[nbx](i, j, k, 2) = quiescent[0];
655 });
656 amrex::Gpu::streamSynchronize();
657#else
658 amrex::ignore_unused(data, level, geom, multiphase_mode);
659#endif
660 }
661}; // namespace ocean_waves
662
663template <>
665{
666 // cppcheck-suppress constParameterReference
667 void operator()(W2AWaves::DataType& data, const amrex::Real time)
668 {
669
670#ifdef AMR_WIND_USE_W2A
671 auto& wdata = data.meta();
672 auto& sim = data.sim();
673
674 // Update ow fields every time
675 auto& ow_levelset = sim.repo().get_field("ow_levelset");
676 auto& ow_velocity = sim.repo().get_field("ow_velocity");
677 // Update HOS fields when necessary
678 auto& w2a_levelset = sim.repo().get_field("w2a_levelset");
679 auto& w2a_velocity = sim.repo().get_field("w2a_velocity");
680
681 // Proxy for multiphase mode of ocean waves
682 bool vof_exists = sim.repo().field_exists("vof");
683
684 auto nlevels = sim.repo().num_active_levels();
685 auto geom = sim.mesh().Geom();
686
687 // Get value for time interpolation
688 amrex::Real t_last = wdata.t_last;
689
690 // Check if new HOS data needs to be read
691 bool read_flag = false;
692 // Check if time indicates reading must take place
693 int new_ntime =
694 wdata.is_ocean
695 ? wdata.c_rmodes.time2step(time + wdata.t_winit, wdata.ntime)
696 : wdata.r_rmodes.time2step(time + wdata.t_winit, wdata.ntime);
697 int double_data = evaluate_read_resize(
698 wdata.ntime, read_flag, wdata.resize_flag, wdata.t, wdata.t_last,
699 new_ntime, wdata.t_winit, wdata.dt_modes, time);
700 // Check if stop time is exceeded, introduce offset to ntime
701 if (read_flag) {
702 // Need to only check when reading is occurring
703 if (wdata.ntime + wdata.n_offset > wdata.n_wstop) {
704 // If exceeding stop step, calculate new offset
705 wdata.n_offset =
706 update_offset_timestep(wdata.ntime, wdata.n_winit);
707 // Print warning to screen
708 amrex::Print()
709 << "WARNING (waves2amr_ops): available mode data exceeded, "
710 "resetting to beginning of mode data.\n";
711 }
712 }
713 // Resizing (assuming reading is taking place) must happen after regrid
714 if (wdata.regrid_occurred) {
715 // resize_flag remains true until resizing occurs, but
716 // regrid_occurred resets every timestep
717 wdata.resize_flag = true;
718 }
719
720 // Read HOS data if necessary based on time
721 if (read_flag) {
722
723 if (wdata.resize_flag) {
724 // Reset flag
725 wdata.resize_flag = false;
726 // Flags for indicating overlap, assume none at first
727 bool flag_z = false;
728 bool flag_xlo = false;
729 bool flag_xhi = false;
730 // Get heights for this processor, check overlap in z
731 flag_z =
732 (interp_to_mfab::get_local_height_indices(
733 wdata.indvec, wdata.hvec, ow_velocity.vec_ptrs(),
734 geom) == 1);
735 // No overlap from heights definitely means no interp
736
737 // Check lateral bounds (in x)
738 const int dir = 0;
739 flag_xlo =
740 (interp_to_mfab::check_lateral_overlap_lo(
741 wdata.gen_length, dir, ow_velocity.vec_ptrs(), geom) ==
742 1);
743 // No overlap with gen region means no interp, unless ...
744 if (wdata.has_outprofile) {
745 // ... if overlap exists here, needing interp
746 flag_xhi =
747 (interp_to_mfab::check_lateral_overlap_hi(
748 wdata.beach_length, dir, ow_velocity.vec_ptrs(),
749 geom) == 1);
750 }
751
752 // Wave generation zones are irrelevant for single-phase mode,
753 // indicated by nonexistence of vof
754 if (!vof_exists) {
755 flag_xlo = true;
756 flag_xhi = true;
757 }
758
759 if (flag_z && (flag_xlo || flag_xhi)) {
760 // Interpolation is needed
761 wdata.do_interp = true;
762 // Do resizing
763 wdata.sp_eta_vec.resize(
764 static_cast<size_t>(wdata.n0) *
765 static_cast<size_t>(wdata.n1),
766 0.0);
767 wdata.sp_u_vec.resize(
768 static_cast<size_t>(wdata.n0 * wdata.n1) *
769 wdata.indvec.size());
770 wdata.sp_v_vec.resize(
771 static_cast<size_t>(wdata.n0 * wdata.n1) *
772 wdata.indvec.size());
773 wdata.sp_w_vec.resize(
774 static_cast<size_t>(wdata.n0 * wdata.n1) *
775 wdata.indvec.size());
776 // Sizes will remain constant and need for interpolation
777 // will remain until a regrid occurs
778 } else {
779 // No overlapping with spatial data or no overlapping with
780 // relaxation zones, interpolation can be skipped
781 wdata.do_interp = false;
782 }
783 }
784 // Only perform reading where needed, communicate offset though
785 amrex::ParallelDescriptor::ReduceIntMax(wdata.n_offset);
786
787 // If double read is required, then copy older wave data to ow_
788 // fields and modify interpolation parameters to get things right
789 if (double_data == 1) {
790 if (wdata.do_interp) {
791 populate_fields_all_levels(
792 wdata, geom, ow_levelset, ow_velocity, -1);
793 }
794
795 // Average down to get fine information on coarse grid where
796 // possible (may be unnecessary)
797 for (int lev = nlevels - 1; lev > 0; --lev) {
798 amrex::average_down(
799 ow_velocity(lev), ow_velocity(lev - 1), 0,
800 AMREX_SPACEDIM, sim.mesh().refRatio(lev - 1));
801 amrex::average_down(
802 ow_levelset(lev), ow_levelset(lev - 1), 0, 1,
803 sim.mesh().refRatio(lev - 1));
804 }
805 // Fill patch to get correct ghost cells after average down
806 ow_velocity.fillpatch(sim.time().new_time());
807 ow_levelset.fillpatch(sim.time().new_time());
808
809 // Prior t_last (corresponding to ow fields)
810 t_last = (wdata.ntime - 1) * wdata.dt_modes;
811 } else if (double_data == 2) {
812 // Restarting simulation or taking a big step, new time at ntime
813 // Initialize ow fields to 0 for time interp, will be replaced
814 if (wdata.do_interp) {
815 ow_levelset.setVal(0.0, ow_levelset.num_grow()[0]);
816 ow_velocity.setVal(0.0, ow_levelset.num_grow()[0]);
817 }
818 // No modification needed for t_last, leads to interp factor = 1
819 }
820
821 // After possible prior read, now read data for this ntime
822 if (wdata.do_interp) {
823 populate_fields_all_levels(
824 wdata, geom, w2a_levelset, w2a_velocity);
825 }
826
827 // Average down to get fine information on coarse grid where
828 // possible and update ghost cells (may be unnecessary)
829 for (int lev = nlevels - 1; lev > 0; --lev) {
830 amrex::average_down(
831 w2a_velocity(lev), w2a_velocity(lev - 1), 0, AMREX_SPACEDIM,
832 sim.mesh().refRatio(lev - 1));
833 w2a_velocity(lev - 1).FillBoundary(geom[lev - 1].periodicity());
834 amrex::average_down(
835 w2a_levelset(lev), w2a_levelset(lev - 1), 0, 1,
836 sim.mesh().refRatio(lev - 1));
837 w2a_levelset(lev - 1).FillBoundary(geom[lev - 1].periodicity());
838 }
839 }
840
841 // Temporally interpolate at every timestep to get target solution
842 for (int lev = 0; lev < nlevels; ++lev) {
843 auto phi = ow_levelset(lev).arrays();
844 auto vel = ow_velocity(lev).arrays();
845 const auto W2A_phi = w2a_levelset(lev).const_arrays();
846 const auto W2A_vel = w2a_velocity(lev).const_arrays();
847
848 const amrex::Real W2A_t = wdata.t;
849 amrex::ParallelFor(
850 ow_levelset(lev), amrex::IntVect(3),
851 [=] AMREX_GPU_DEVICE(int nbx, int i, int j, int k) noexcept {
852 // Interpolate temporally every time
853 phi[nbx](i, j, k) +=
854 (W2A_phi[nbx](i, j, k) - phi[nbx](i, j, k)) *
855 (time - t_last) / (W2A_t - t_last + 1e-16);
856 vel[nbx](i, j, k, 0) +=
857 (W2A_vel[nbx](i, j, k, 0) - vel[nbx](i, j, k, 0)) *
858 (time - t_last) / (W2A_t - t_last + 1e-16);
859 vel[nbx](i, j, k, 1) +=
860 (W2A_vel[nbx](i, j, k, 1) - vel[nbx](i, j, k, 1)) *
861 (time - t_last) / (W2A_t - t_last + 1e-16);
862 vel[nbx](i, j, k, 2) +=
863 (W2A_vel[nbx](i, j, k, 2) - vel[nbx](i, j, k, 2)) *
864 (time - t_last) / (W2A_t - t_last + 1e-16);
865 });
866 }
867 amrex::Gpu::streamSynchronize();
868#else
869 amrex::ignore_unused(data, time);
870#endif
871 }
872};
873
874} // namespace amr_wind::ocean_waves::ops
875
876#endif /* WAVES2AMR_OPS_H */
FieldRepo & repo()
Return the field repository.
Definition CFDSim.H:75
SimTime & time()
Return simulation time control.
Definition CFDSim.H:65
Definition Field.H:116
FieldRepo & repo() const
FieldRepo instance that manages this field.
Definition Field.H:159
void set_default_fillpatch_bc(const SimTime &time, const amrex::BCType::mathematicalBndryTypes bctype=amrex::BCType::hoextrap) noexcept
Definition Field.cpp:377
amrex::Vector< amrex::MultiFab * > vec_ptrs() noexcept
Return a vector of MultiFab pointers for all levels.
Definition Field.cpp:142
Field & declare_field(const std::string &name, const int ncomp=1, const int ngrow=0, const int nstates=1, const FieldLoc floc=FieldLoc::CELL)
Definition FieldRepo.cpp:83
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
Definition OceanWavesTypes.H:59
OceanWavesTrait::MetaType & meta()
Definition OceanWavesTypes.H:89
CFDSim & sim()
Definition OceanWavesTypes.H:83
OceanWavesTrait::InfoType & info()
Definition OceanWavesTypes.H:86
static constexpr amrex::Real TIGHT_TOL
A tight tolerance.
Definition constants.H:19
Definition OceanWavesOps.H:8
void read_inputs(RelaxZonesBaseData &wdata, OceanWavesInfo &, const ::amr_wind::utils::MultiParser &pp)
Definition relaxation_zones_ops.cpp:15
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE WaveVec harmonize_profiles_1d(const amrex::Real x, const amrex::Real left_bdy, const amrex::Real left_length, const amrex::Real right_bdy, const amrex::Real right_length, const WaveVec left, const WaveVec bulk, const WaveVec right)
Definition wave_utils_K.H:67
amrex::GpuArray< amrex::Real, 4 > WaveVec
Definition wave_utils_K.H:9
bool has_beach
Definition RelaxationZones.H:36
bool has_outprofile
Definition RelaxationZones.H:37
amrex::Real beach_length
Definition RelaxationZones.H:27
bool init_wave_field
Definition RelaxationZones.H:32
amrex::Real current
Definition RelaxationZones.H:30
amrex::Real gen_length
Definition RelaxationZones.H:25
bool regrid_occurred
Definition RelaxationZones.H:41
amrex::Real zsl
Definition RelaxationZones.H:17
Definition W2AWaves.H:14
amrex::Real xlo1
Definition W2AWaves.H:29
std::vector< std::complex< double > > c_mY
Definition W2AWaves.H:54
amrex::Real dimL
Definition W2AWaves.H:31
amrex::Real dt_modes
Definition W2AWaves.H:33
amrex::Gpu::DeviceVector< amrex::Real > sp_v_vec
Definition W2AWaves.H:84
std::vector< double > r_mAdd
Definition W2AWaves.H:55
amrex::Vector< int > indvec
Definition W2AWaves.H:76
std::vector< double > r_mX
Definition W2AWaves.H:55
int n1
Definition W2AWaves.H:19
amrex::Gpu::DeviceVector< amrex::Real > sp_u_vec
Definition W2AWaves.H:84
int n_winit
Definition W2AWaves.H:41
amrex::Vector< amrex::Real > hvec
Definition W2AWaves.H:74
int n_offset
Definition W2AWaves.H:45
amrex::Real dx0
Definition W2AWaves.H:25
int ntime
Definition W2AWaves.H:39
int n1_sp
Definition W2AWaves.H:23
amrex::Real t_last
Definition W2AWaves.H:49
amrex::Real xlo0
Definition W2AWaves.H:28
amrex::Gpu::DeviceVector< amrex::Real > sp_w_vec
Definition W2AWaves.H:85
int n0
Definition W2AWaves.H:18
int n0_sp
Definition W2AWaves.H:22
amrex::Real dx1
Definition W2AWaves.H:26
amrex::Real t_winit
Definition W2AWaves.H:37
int n2
Definition W2AWaves.H:20
std::vector< std::complex< double > > c_mZ
Definition W2AWaves.H:54
int n_wstop
Definition W2AWaves.H:43
std::vector< std::complex< double > > c_mFS
Definition W2AWaves.H:54
std::vector< double > r_mZ
Definition W2AWaves.H:55
std::vector< std::complex< double > > c_mX
Definition W2AWaves.H:54
std::string modes_file
Definition W2AWaves.H:16
bool is_ocean
Definition W2AWaves.H:78
std::vector< double > r_mFS
Definition W2AWaves.H:55
amrex::Gpu::DeviceVector< amrex::Real > sp_eta_vec
Definition W2AWaves.H:84
bool do_interp
Definition W2AWaves.H:80
std::vector< double > r_mY
Definition W2AWaves.H:55
bool resize_flag
Definition W2AWaves.H:82
amrex::Real t
Definition W2AWaves.H:51
Definition W2AWaves.H:132
void operator()(W2AWaves::DataType &data, int level, const amrex::Geometry &geom, bool multiphase_mode)
Definition waves2amr_ops.H:551
Definition OceanWavesOps.H:14
void operator()(W2AWaves::DataType &data, const ::amr_wind::utils::MultiParser &pp)
Definition waves2amr_ops.H:223
Definition OceanWavesOps.H:11
void operator()(W2AWaves::DataType &data, const amrex::Real time)
Definition waves2amr_ops.H:667