1 #ifndef ERF_PROBCOMMON_H_
2 #define ERF_PROBCOMMON_H_
7 #include <AMReX_ParmParse.H>
8 #include <AMReX_Geometry.H>
9 #include <AMReX_FArrayBox.H>
10 #include <AMReX_MultiFab.H>
11 #include <AMReX_Reduce.H>
33 amrex::FArrayBox& terrain_fab,
58 std::unique_ptr<amrex::MultiFab>& ,
59 std::unique_ptr<amrex::MultiFab>& ,
60 amrex::Geometry
const& ,
61 const amrex::Vector<amrex::Real>& ,
64 amrex::Print() <<
"Hydrostatically balanced density was NOT set"
65 <<
" -- an appropriate init_type should probably have been specified"
66 <<
" (e.g., input_sounding, WRFInput, or Metgrid)"
68 amrex::Error(
"Should never call this version of erf_init_dens_hse_dry for "+
name()+
" problem");
74 amrex::Error(
"Should never call this version of erf_init_const_dens_hse for "+
name()+
" problem");
79 amrex::MultiFab& , amrex::MultiFab& ,
82 amrex::Error(
"Should never call this version of erf_init_const_dens_and_th_hse for "+
name()+
" problem");
87 amrex::MultiFab& , amrex::MultiFab& ,
89 std::unique_ptr<amrex::MultiFab>& )
91 amrex::Error(
"Should never call this version of erf_init_const_dens_and_linear_th_hse for "+
name()+
" problem");
96 std::unique_ptr<amrex::MultiFab>& ,
97 amrex::Geometry
const& ,
98 const amrex::Vector<amrex::Real>& )
122 amrex::Array4<amrex::Real const>
const& ,
123 amrex::Array4<amrex::Real >
const& ,
124 amrex::Array4<amrex::Real >
const& ,
125 amrex::Array4<amrex::Real >
const& ,
126 amrex::Array4<amrex::Real const>
const& ,
127 amrex::Array4<amrex::Real const>
const& ,
128 amrex::Array4<amrex::Real const>
const& ,
129 amrex::GeometryData
const& ,
130 amrex::Array4<amrex::Real const>
const& ,
133 amrex::Print() <<
"No perturbation to background fields supplied for "
134 <<
name() <<
" problem" << std::endl;
159 amrex::Array4<amrex::Real >
const& ,
160 amrex::Array4<amrex::Real >
const& ,
161 amrex::Array4<amrex::Real >
const& ,
162 amrex::Array4<amrex::Real const>
const& ,
163 amrex::Array4<amrex::Real const>
const& ,
164 amrex::GeometryData
const& ,
165 amrex::Array4<amrex::Real const>
const& ,
166 amrex::Array4<amrex::Real const>
const& ,
169 amrex::Print() <<
"No perturbation velocities supplied for " <<
name() <<
" problem" << std::endl;
184 amrex::MultiFab* src,
185 const amrex::Geometry& ,
186 std::unique_ptr<amrex::MultiFab>& ,
187 const amrex::Vector<amrex::Real>& )
189 if (src->empty())
return;
191 amrex::Warning(
"Temperature forcing not defined for "+
name()+
" problem");
192 for ( amrex::MFIter mfi(*src, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
194 const auto &box = mfi.tilebox();
195 const amrex::Array4<amrex::Real>& src_arr = src->array(mfi);
198 ParallelFor(box, [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) noexcept {
201 src_arr(i, j, k) =
zero;
218 amrex::MultiFab* qsrc,
219 const amrex::Geometry& ,
220 std::unique_ptr<amrex::MultiFab>& ,
221 const amrex::Vector<amrex::Real>& )
223 if (qsrc->empty())
return;
225 amrex::Warning(
"Moisture forcing not defined for "+
name()+
" problem");
226 for ( amrex::MFIter mfi(*qsrc, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
228 const auto &box = mfi.tilebox();
229 const amrex::Array4<amrex::Real>& qsrc_arr = qsrc->array(mfi);
232 ParallelFor(box, [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) noexcept {
235 qsrc_arr(i, j, k) =
zero;
255 amrex::Vector<amrex::Real>&
wbar,
256 amrex::Gpu::DeviceVector<amrex::Real>& d_wbar,
257 const amrex::MultiFab& ,
258 const amrex::Geometry& geom,
259 std::unique_ptr<amrex::MultiFab>& ,
260 const amrex::Vector<amrex::Real>& )
262 if (
wbar.empty())
return;
264 amrex::Warning(
"Moisture forcing not defined for "+
name()+
" problem");
266 const int khi = geom.Domain().bigEnd()[2];
269 for (
int k = 0; k <=
khi; k++)
277 amrex::Gpu::copy(amrex::Gpu::hostToDevice,
wbar.begin(),
wbar.end(), d_wbar.begin());
292 amrex::Vector<amrex::Real>& u_geos,
293 amrex::Gpu::DeviceVector<amrex::Real>& d_u_geos,
294 amrex::Vector<amrex::Real>& v_geos,
295 amrex::Gpu::DeviceVector<amrex::Real>& d_v_geos,
296 const amrex::Geometry& geom,
297 std::unique_ptr<amrex::MultiFab>& ,
298 const amrex::Vector<amrex::Real>& )
300 if (u_geos.empty())
return;
302 amrex::Warning(
"Geostrophic wind profile not defined for "+
name()+
" problem");
304 const int khi = geom.Domain().bigEnd()[2];
307 for (
int k = 0; k <=
khi; k++)
316 amrex::Gpu::copy(amrex::Gpu::hostToDevice, u_geos.begin(), u_geos.end(), d_u_geos.begin());
317 amrex::Gpu::copy(amrex::Gpu::hostToDevice, v_geos.begin(), v_geos.end(), d_v_geos.begin());
329 amrex::FArrayBox& buildings_fab,
334 amrex::ParmParse
pp(
"erf");
335 auto valid_fname =
pp.queryAdd(
"buildings_file_name",fname);
359 amrex::FArrayBox& terrain_fab,
362 amrex::ParmParse
pp(
"erf");
365 std::string fname_nc;
366 auto valid_fname_nc =
pp.query(
"terrain_file_name_nc", fname_nc);
368 std::string fname, fname_usgs;
369 auto valid_fname =
pp.query(
"terrain_file_name", fname);
370 auto valid_fname_USGS =
pp.query(
"terrain_file_name_USGS", fname_usgs);
372 if (valid_fname_nc) {
373 #ifdef ERF_USE_NETCDF
374 read_terrain_netcdf(fname_nc, geom, terrain_fab, time);
376 amrex::Abort(
"ERF must be compiled with NetCDF support to use terrain_file_name_nc");
378 }
else if (valid_fname) {
381 }
else if (valid_fname_USGS) {
408 amrex::FArrayBox& terrain_fab)
410 const bool per_x = geom.isPeriodic(0);
411 const bool per_y = geom.isPeriodic(1);
412 if (!per_x && !per_y) {
return; }
414 const amrex::Box& domain = geom.Domain();
415 const int ilo = domain.smallEnd(0);
const int nx = domain.length(0);
416 const int jlo = domain.smallEnd(1);
const int ny = domain.length(1);
418 const amrex::Box bx = terrain_fab.box();
419 const auto blo = amrex::lbound(bx);
420 const auto bhi = amrex::ubound(bx);
421 amrex::Array4<amrex::Real>
const& z_arr = terrain_fab.array();
424 (!per_x || (blo.x <= ilo && bhi.x >= ilo+
nx-1)) &&
425 (!per_y || (blo.y <= jlo && bhi.y >= jlo+
ny-1)),
426 "make_terrain_surface_periodic: the terrain FArrayBox must hold every node of the domain in a periodic direction");
431 amrex::Box hi_bx = bx & amrex::surroundingNodes(domain);
432 hi_bx.setSmall(2, bx.smallEnd(2)); hi_bx.setBig(2, bx.smallEnd(2));
433 if (!hi_bx.isEmpty()) {
434 amrex::ReduceOps<amrex::ReduceOpMax> reduce_op;
435 amrex::ReduceData<amrex::Real> reduce_data(reduce_op);
436 reduce_op.eval(hi_bx, reduce_data,
437 [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) -> amrex::GpuTuple<amrex::Real>
439 int ii = (per_x) ? ilo + ((i-ilo) %
nx +
nx) %
nx : i;
440 int jj = (per_y) ? jlo + ((j-jlo) %
ny +
ny) %
ny : j;
441 return { (ii != i || jj != j) ? amrex::Math::abs(z_arr(i,j,k) - z_arr(ii,jj,k))
446 const amrex::Real max_diff = amrex::get<0>(reduce_data.value());
447 static bool warned =
false;
448 if (!warned && max_diff >
amrex::Real(0.1) * geom.CellSize(2)) {
450 amrex::Print() <<
"WARNING: the terrain surface is not periodic in a periodic direction: "
451 <<
"a node on the high side of the domain differs from its image on the low "
452 <<
"side by up to " << max_diff <<
" m. The low side is used for both, so the "
453 <<
"cells next to the high side see a step in the surface.\n";
461 int ii = (per_x) ? ilo + ((i-ilo) %
nx +
nx) %
nx : i;
462 int jj = (per_y) ? jlo + ((j-jlo) %
ny +
ny) %
ny : j;
463 if ((ii != i || jj != j) &&
464 ii >= blo.x && ii <= bhi.x && jj >= blo.y && jj <= bhi.y) {
465 z_arr(i,j,k) = z_arr(ii,jj,k);
482 amrex::ParmParse
pp(
"erf");
484 if (
pp.query(
"terrain_file_name_nc", fname) ||
485 pp.query(
"terrain_file_name", fname) ||
486 pp.query(
"terrain_file_name_USGS", fname)) {
490 std::string custom_terrain_type =
"None";
491 amrex::ParmParse
pp_prob(
"prob");
492 pp_prob.query(
"custom_terrain_type", custom_terrain_type);
493 return (custom_terrain_type !=
"None");
496 #ifdef ERF_USE_NETCDF
498 read_terrain_netcdf (
const std::string& fname,
499 const amrex::Geometry& geom,
500 amrex::FArrayBox& terrain_fab,
503 amrex::Print() <<
"Reading terrain from NetCDF file: " << fname << std::endl;
505 amrex::Vector<amrex::Real> m_xterrain, m_yterrain, m_zterrain;
509 int mass_point_terrain = 0;
511 if (amrex::ParallelDescriptor::IOProcessor()) {
512 auto ncf = ncutils::NCFile::open(fname, NC_NOWRITE);
515 std::string height_varname =
"height";
516 if (!ncf.has_var(
"height")) {
517 if (ncf.has_var(
"z")) height_varname =
"z";
518 else if (ncf.has_var(
"terrain")) height_varname =
"terrain";
519 else if (ncf.has_var(
"HGT_M")) height_varname =
"HGT_M";
520 else if (ncf.has_var(
"AGL")) height_varname =
"AGL";
522 amrex::Abort(
"No terrain height variable found in NetCDF file. "
523 "Expected: 'height', 'z', 'terrain', 'HGT_M', or 'AGL'");
525 amrex::Print() <<
" Using terrain variable: " << height_varname << std::endl;
527 auto var = ncf.var(height_varname);
528 auto dims = var.shape();
529 const auto dim_names = var.dimnames();
530 const auto is_y_dimension = [] (
const std::string&
name) {
531 return name ==
"y" ||
name ==
"south_north";
533 const auto is_x_dimension = [] (
const std::string&
name) {
534 return name ==
"x" ||
name ==
"west_east";
536 const auto is_time_dimension = [] (
const std::string&
name) {
537 return name ==
"time" ||
name ==
"Time";
540 bool has_time_dimension =
false;
541 if (dims.size() == 2) {
542 if (dim_names.size() != 2 || !is_y_dimension(dim_names[0]) ||
543 !is_x_dimension(dim_names[1])) {
544 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
545 fname +
"' must use dimensions (y,x) or "
546 "(south_north,west_east)");
550 }
else if (dims.size() == 3) {
551 if (dim_names.size() != 3 || !is_time_dimension(dim_names[0]) ||
552 !is_y_dimension(dim_names[1]) || !is_x_dimension(dim_names[2])) {
553 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
554 fname +
"' must use dimensions (time,y,x) with time leading; "
555 "accepted layouts are (y,x) and (time,y,x)");
558 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
559 fname +
"' has no time records");
561 has_time_dimension =
true;
565 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" + fname +
566 "' has unsupported rank " + std::to_string(dims.size()) +
567 "; accepted layouts are (y,x) and (time,y,x)");
570 if (
nx < 2 ||
ny < 2) {
571 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
572 fname +
"' requires at least two x and y coordinates "
573 "for bilinear interpolation");
576 const bool has_x = ncf.has_var(
"x");
577 const bool has_y = ncf.has_var(
"y");
578 if (has_x != has_y) {
579 amrex::Abort(
"NetCDF terrain file '" + fname +
580 "' must provide both one-dimensional 'x' and 'y' coordinates");
583 const std::string terrain_description =
584 "NetCDF terrain field '" + height_varname +
"' in '" + fname +
"'";
585 m_zterrain.resize(
nx *
ny);
587 if (height_varname !=
"HGT_M" || !has_time_dimension ||
588 dim_names != std::vector<std::string>{
"Time",
"south_north",
"west_east"}) {
589 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" + fname +
590 "' requires one-dimensional 'x' and 'y' coordinates; a genuine "
591 "WPS file must provide HGT_M(Time,south_north,west_east)");
594 const auto require_real_attribute = [&] (
const std::string&
name) {
595 if (!ncf.has_attr(
name)) {
596 amrex::Abort(
"WPS terrain file '" + fname +
597 "' is missing required global attribute '" +
name +
"'");
599 std::vector<double> values;
600 ncf.get_attr(
name, values);
601 if (values.size() != 1 || !std::isfinite(values[0])) {
602 amrex::Abort(
"WPS terrain file '" + fname +
603 "' must have one finite global attribute '" +
name +
"'");
607 const auto require_int_attribute = [&] (
const std::string&
name) {
608 if (!ncf.has_attr(
name)) {
609 amrex::Abort(
"WPS terrain file '" + fname +
610 "' is missing required global attribute '" +
name +
"'");
612 std::vector<int> values;
613 ncf.get_attr(
name, values);
614 if (values.size() != 1) {
615 amrex::Abort(
"WPS terrain file '" + fname +
616 "' must have one global attribute '" +
name +
"'");
621 const int map_proj = require_int_attribute(
"MAP_PROJ");
632 if (map_proj != 1 && map_proj != 2 && map_proj != 3) {
635 ?
"; latitude/longitude WPS grids are not supported because their "
636 "DX/DY are in degrees rather than metres"
637 :
"; supported projections are 1 (Lambert), 2 (polar), "
639 amrex::Abort(
"WPS terrain file '" + fname +
"' uses unsupported MAP_PROJ=" +
640 std::to_string(map_proj) + reason);
642 const double cen_lat = require_real_attribute(
"CEN_LAT");
643 const double cen_lon = require_real_attribute(
"CEN_LON");
644 const double stand_lon = require_real_attribute(
"STAND_LON");
645 const double wps_dx = require_real_attribute(
"DX");
646 const double wps_dy = require_real_attribute(
"DY");
647 if (wps_dx <= 0.0 || wps_dy <= 0.0 || std::abs(cen_lat) > 90.0 ||
648 std::abs(cen_lon) > 360.0 || std::abs(stand_lon) > 360.0) {
649 amrex::Abort(
"WPS terrain file '" + fname +
650 "' has invalid projection or grid metadata");
653 const double truelat1 = require_real_attribute(
"TRUELAT1");
654 if (std::abs(truelat1) > 90.0) {
655 amrex::Abort(
"WPS terrain file '" + fname +
656 "' has invalid TRUELAT1");
659 const double truelat2 = require_real_attribute(
"TRUELAT2");
660 if (std::abs(truelat2) > 90.0) {
661 amrex::Abort(
"WPS terrain file '" + fname +
662 "' has invalid TRUELAT2");
666 const int wps_nx = require_int_attribute(
"WEST-EAST_GRID_DIMENSION");
667 const int wps_ny = require_int_attribute(
"SOUTH-NORTH_GRID_DIMENSION");
668 if (wps_nx !=
nx + 1 || wps_ny !=
ny + 1) {
669 amrex::Abort(
"WPS terrain file '" + fname +
670 "' has grid dimensions inconsistent with HGT_M: expected " +
671 std::to_string(
nx + 1) +
" x " + std::to_string(
ny + 1));
674 const auto validate_wps_coordinate = [&] (
const std::string&
name) {
675 if (!ncf.has_var(
name)) {
676 amrex::Abort(
"WPS terrain file '" + fname +
677 "' is missing required coordinate variable '" +
name +
"'");
679 const auto coordinate = ncf.var(
name);
680 if (coordinate.shape() != dims ||
681 coordinate.dimnames() != dim_names) {
682 amrex::Abort(
"WPS terrain coordinate '" +
name +
"' in '" + fname +
683 "' must have dimensions (Time,south_north,west_east) "
686 amrex::Vector<amrex::Real> values(
nx *
ny);
687 const std::vector<size_t> start{0, 0, 0};
688 const std::vector<size_t> count{
689 1,
static_cast<size_t>(
ny),
static_cast<size_t>(
nx)};
690 coordinate.get(values.data(), start, count);
691 if (!std::all_of(values.begin(), values.end(),
692 [] (
amrex::Real value) { return std::isfinite(value); })) {
693 amrex::Abort(
"WPS terrain coordinate '" +
name +
"' in '" + fname +
694 "' contains nonfinite values");
697 validate_wps_coordinate(
"XLAT_M");
698 validate_wps_coordinate(
"XLONG_M");
700 const auto dx = geom.CellSizeArray();
701 const auto close_enough = [] (
amrex::Real lhs,
double rhs) {
704 return std::abs(lhs -
static_cast<amrex::Real>(rhs)) <=
707 if (geom.Domain().length(0) !=
nx || geom.Domain().length(1) !=
ny ||
708 !close_enough(
dx[0], wps_dx) || !close_enough(
dx[1], wps_dy) ||
709 !close_enough(geom.ProbHi(0) - geom.ProbLo(0), wps_dx *
nx) ||
710 !close_enough(geom.ProbHi(1) - geom.ProbLo(1), wps_dy *
ny)) {
711 amrex::Abort(
"WPS terrain grid does not match the ERF Geometry; "
712 "HGT_M dimensions, DX/DY, and the ERF domain extent must agree");
720 m_xterrain.resize(
nx);
721 m_yterrain.resize(
ny);
722 for (
int i = 0; i <
nx; ++i) {
723 m_xterrain[i] = geom.ProbLo(0) +
727 for (
int j = 0; j <
ny; ++j) {
728 m_yterrain[j] = geom.ProbLo(1) +
732 const std::vector<size_t> start{0, 0, 0};
733 const std::vector<size_t> count{
734 1,
static_cast<size_t>(
ny),
static_cast<size_t>(
nx)};
735 var.get(m_zterrain.data(), start, count);
736 mass_point_terrain = 1;
738 const auto x_shape = ncf.var(
"x").shape();
739 const auto y_shape = ncf.var(
"y").shape();
740 if (x_shape.size() != 1 || x_shape[0] !=
static_cast<std::size_t
>(
nx)) {
741 amrex::Abort(
"NetCDF terrain x coordinate in '" + fname +
742 "' must be one-dimensional with length " + std::to_string(
nx));
744 if (y_shape.size() != 1 || y_shape[0] !=
static_cast<std::size_t
>(
ny)) {
745 amrex::Abort(
"NetCDF terrain y coordinate in '" + fname +
746 "' must be one-dimensional with length " + std::to_string(
ny));
749 m_xterrain.resize(
nx);
750 m_yterrain.resize(
ny);
751 ncf.var(
"x").get(m_xterrain.data());
752 ncf.var(
"y").get(m_yterrain.data());
754 amrex::Real x_origin, x_spacing, y_origin, y_spacing;
756 m_xterrain,
nx,
"x", terrain_description, x_origin, x_spacing);
757 if (coordinate_error.empty()) {
759 m_yterrain,
ny,
"y", terrain_description, y_origin, y_spacing);
761 if (!coordinate_error.empty()) {
762 amrex::Abort(coordinate_error);
765 if (has_time_dimension) {
766 const std::vector<size_t> start{0, 0, 0};
767 const std::vector<size_t> count{
768 1,
static_cast<size_t>(
ny),
static_cast<size_t>(
nx)};
769 var.get(m_zterrain.data(), start, count);
771 var.get(m_zterrain.data());
774 if (!std::all_of(m_zterrain.begin(), m_zterrain.end(),
775 [] (
amrex::Real value) { return std::isfinite(value); })) {
776 amrex::Abort(terrain_description +
" contains nonfinite height values");
780 amrex::Print() <<
" Grid size: " <<
nx <<
" x " <<
ny <<
"\n"
781 <<
" X range: [" << m_xterrain[0] <<
", " << m_xterrain[
nx-1] <<
"]\n"
782 <<
" Y range: [" << m_yterrain[0] <<
", " << m_yterrain[
ny-1] <<
"]\n"
783 <<
" Height range: ["
784 << *std::min_element(m_zterrain.begin(), m_zterrain.end()) <<
", "
785 << *std::max_element(m_zterrain.begin(), m_zterrain.end()) <<
"]\n";
789 amrex::ParallelDescriptor::Bcast(&
nx, 1, amrex::ParallelDescriptor::IOProcessorNumber());
790 amrex::ParallelDescriptor::Bcast(&
ny, 1, amrex::ParallelDescriptor::IOProcessorNumber());
791 amrex::ParallelDescriptor::Bcast(&mass_point_terrain, 1,
792 amrex::ParallelDescriptor::IOProcessorNumber());
794 if (!amrex::ParallelDescriptor::IOProcessor()) {
795 m_xterrain.resize(
nx);
796 m_yterrain.resize(
ny);
797 m_zterrain.resize(
nx *
ny);
800 amrex::ParallelDescriptor::Bcast(m_xterrain.data(),
nx, amrex::ParallelDescriptor::IOProcessorNumber());
801 amrex::ParallelDescriptor::Bcast(m_yterrain.data(),
ny, amrex::ParallelDescriptor::IOProcessorNumber());
802 amrex::ParallelDescriptor::Bcast(m_zterrain.data(),
nx *
ny, amrex::ParallelDescriptor::IOProcessorNumber());
805 amrex::Gpu::DeviceVector<amrex::Real> d_xterrain(
nx), d_yterrain(
ny), d_zterrain(
nx *
ny);
806 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_xterrain.begin(), m_xterrain.end(), d_xterrain.begin());
807 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_yterrain.begin(), m_yterrain.end(), d_yterrain.begin());
808 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_zterrain.begin(), m_zterrain.end(), d_zterrain.begin());
814 auto dx_arr = geom.CellSizeArray();
815 auto prob_lo = geom.ProbLoArray();
817 int ilo = geom.Domain().smallEnd(0);
818 int jlo = geom.Domain().smallEnd(1);
819 int klo = geom.Domain().smallEnd(2);
820 int ihi = geom.Domain().bigEnd(0) + 1;
821 int jhi = geom.Domain().bigEnd(1) + 1;
823 amrex::Box zbx = terrain_fab.box();
824 amrex::Array4<amrex::Real>
const& z_arr = terrain_fab.array();
825 const amrex::Real terrain_dx = m_xterrain[1] - m_xterrain[0];
826 const amrex::Real terrain_dy = m_yterrain[1] - m_yterrain[0];
828 if (!mass_point_terrain && !zbx.isEmpty()) {
829 amrex::Box target_box = zbx & amrex::convert(geom.Domain(), zbx.ixType());
830 if (!target_box.isEmpty()) {
831 target_box.setSmall(2, zbx.smallEnd(2));
832 target_box.setBig(2, zbx.smallEnd(2));
835 for (
int d = 0; d < 2; ++d) {
836 if (geom.isPeriodic(d)) {
837 target_box.setBig(d, amrex::min(target_box.bigEnd(d), geom.Domain().bigEnd(d)));
840 amrex::ReduceOps<amrex::ReduceOpSum, amrex::ReduceOpSum> reduce_op;
841 amrex::ReduceData<amrex::Long, amrex::Long> reduce_data(reduce_op);
842 reduce_op.eval(target_box, reduce_data,
843 [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) -> amrex::GpuTuple<amrex::Long, amrex::Long> {
847 x, d_xt[0], terrain_dx,
nx);
849 y, d_yt[0], terrain_dy,
ny);
850 const amrex::Long outside =
851 (x_stencil.inside && y_stencil.inside) ? amrex::Long(0) : amrex::Long(1);
852 return {amrex::Long(1), outside};
859 const auto local = reduce_data.value();
860 const amrex::Long total_targets = amrex::get<0>(local);
861 const amrex::Long outside_targets = amrex::get<1>(local);
862 if (amrex::ParallelDescriptor::IOProcessor() && outside_targets > 0) {
872 amrex::Print() <<
"WARNING: Terrain source grid does not cover ERF terrain nodes: outside "
873 << outside_targets <<
" of " << total_targets <<
" ("
874 << percentage <<
"%). Source extent x=[" << m_xterrain[0]
875 <<
", " << source_xhi <<
"] y=[" << m_yterrain[0] <<
", "
876 << source_yhi <<
"]; target extent x=[" << target_xlo <<
", "
877 << target_xhi <<
"] y=[" << target_ylo <<
", " << target_yhi
878 <<
"]. Outside nodes are set to zero.\n";
885 int ii = amrex::min(amrex::max(i, ilo), ihi);
886 int jj = amrex::min(amrex::max(j, jlo), jhi);
893 if (mass_point_terrain) {
898 x = amrex::min(amrex::max(
x, d_xt[0]), d_xt[
nx-1]);
899 y = amrex::min(amrex::max(
y, d_yt[0]), d_yt[
ny-1]);
902 x, d_xt[0], dx_t,
nx);
904 y, d_yt[0], dy_t,
ny);
906 if (x_stencil.inside && y_stencil.inside) {
907 const int i_t = x_stencil.lower;
908 const int j_t = y_stencil.lower;
929 amrex::Gpu::synchronize();
936 const amrex::Geometry& geom,
937 amrex::FArrayBox& terrain_fab,
939 bool require_domain_coverage =
true,
940 bool zero_outside_table =
false)
945 geom.ProbLoArray(), geom.ProbHiArray(),
946 require_domain_coverage);
947 const int nx = table.nx;
948 const int ny = table.ny;
949 const amrex::Vector<amrex::Real>& m_xterrain = table.x;
950 const amrex::Vector<amrex::Real>& m_yterrain = table.y;
951 const amrex::Vector<amrex::Real>& m_zterrain = table.z;
953 const int nz =
nx *
ny;
954 const int nx_vals = is_usgs ? (
nx *
ny) :
nx;
957 amrex::Gpu::DeviceVector<amrex::Real> d_xterrain(nx_vals),d_yterrain(
ny),d_zterrain(nz);
958 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_xterrain.begin(), m_xterrain.end(), d_xterrain.begin());
959 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_yterrain.begin(), m_yterrain.end(), d_yterrain.begin());
960 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_zterrain.begin(), m_zterrain.end(), d_zterrain.begin());
966 auto dx = geom.CellSizeArray();
967 auto ProbLoArr = geom.ProbLoArray();
969 int ilo = geom.Domain().smallEnd(0);
970 int jlo = geom.Domain().smallEnd(1);
971 int klo = geom.Domain().smallEnd(2);
972 int ihi = geom.Domain().bigEnd(0) + 1;
973 int jhi = geom.Domain().bigEnd(1) + 1;
975 amrex::Box zbx = terrain_fab.box();
976 amrex::Array4<amrex::Real>
const& z_arr = terrain_fab.array();
981 int ii = amrex::min(amrex::max(i,ilo),ihi);
982 int jj = amrex::min(amrex::max(j,jlo),jhi);
993 if (zero_outside_table) {
994 const bool out_x =
nx > 1 && (x_node < d_xt[0] || x_node > d_xt[
nx-1]);
995 const bool out_y =
ny > 1 && (y_node < d_yt[0] || y_node > d_yt[
ny-1]);
996 if (out_x || out_y) {
1002 int ind11, ind12, ind21, ind22;
1005 int iindex_terrain=-1;
1006 int jindex_terrain=-1;
1016 bool jfound =
false;
1017 for (
int it = 0; it <
ny && !jfound; it++) {
1019 jindex_terrain = it-1; jfound =
true;
1023 jindex_terrain =
ny-1;
1026 jindex_terrain = amrex::min(amrex::max(jindex_terrain,0),
ny-2);
1028 int gstart = (jindex_terrain )*
nx;
1029 int gend = (jindex_terrain+1)*
nx-1;
1030 bool ifound =
false;
1031 for (
int it = gstart; it <= gend && !ifound; it++) {
1033 iindex_terrain = it-gstart-1; ifound =
true;
1037 iindex_terrain =
nx-1;
1040 iindex_terrain = amrex::min(amrex::max(iindex_terrain,0),
nx-2);
1043 ind11 = jindex_terrain*
nx + iindex_terrain;
1050 y1 = d_yt[jindex_terrain];
1051 y2 = d_yt[jindex_terrain+1];
1057 z_arr(i,j,
klo) = d_zt[ind11];
1065 z_arr(i,j,
klo) = w_11*d_zt[ind11] + w_12*d_zt[ind12] + w_21*d_zt[ind21] + w_22*d_zt[ind22];
1070 bool jfound =
false;
1071 for (
int it = 0; it <
ny && !jfound; it++) {
1073 jindex_terrain = it-1; jfound =
true;
1078 jindex_terrain =
ny-1;
1080 jindex_terrain = amrex::max(jindex_terrain,0);
1082 bool ifound =
false;
1083 for (
int it = 0; it <
nx && !ifound; it++) {
1085 iindex_terrain = it-1; ifound =
true;
1090 iindex_terrain =
nx-1;
1092 iindex_terrain = amrex::max(iindex_terrain,0);
1096 int ip1 = amrex::min(iindex_terrain+1,
nx-1);
1097 int jp1 = amrex::min(jindex_terrain+1,
ny-1);
1100 x1 = d_xt[iindex_terrain];
1102 y1 = d_yt[jindex_terrain];
1109 ind11 = iindex_terrain *
ny + jindex_terrain;
1110 ind21 = ip1 *
ny + jindex_terrain;
1112 ind12 = iindex_terrain *
ny + jp1;
1113 ind22 = ip1 *
ny + jp1;
1119 ind11 = jindex_terrain *
nx + iindex_terrain;
1120 ind12 = jp1 *
nx + iindex_terrain;
1122 ind21 = jindex_terrain *
nx + ip1;
1123 ind22 = jp1 *
nx + ip1;
1127 bool interp_x = (ip1 != iindex_terrain) && (x2 != x1);
1128 bool interp_y = (jp1 != jindex_terrain) && (y2 != y1);
1130 if (!interp_x && !interp_y)
1132 z_arr(i,j,
klo) = d_zt[ind11];
1134 else if (interp_x && !interp_y)
1139 z_arr(i,j,
klo) = (w_11*d_zt[ind11] + w_21*d_zt[ind21])/denom;
1141 else if (!interp_x && interp_y)
1146 z_arr(i,j,
klo) = (w_11*d_zt[ind11] + w_12*d_zt[ind12])/denom;
1155 z_arr(i,j,
klo) = (w_11*d_zt[ind11] + w_12*d_zt[ind12] + w_21*d_zt[ind21] + w_22*d_zt[ind22]) / denom;
1161 amrex::Gpu::streamSynchronize();
1175 amrex::FArrayBox& terrain_fab,
1178 std::string custom_terrain_type =
"None";
1179 amrex::ParmParse
pp_prob(
"prob");
pp_prob.queryAdd(
"custom_terrain_type",custom_terrain_type);
1181 if (custom_terrain_type !=
"None")
1183 amrex::Print() <<
"Calling custom terrain initialization" << std::endl;
1187 amrex::Print() <<
"Initializing flat terrain" << std::endl;
1189 terrain_fab.template setVal<amrex::RunOn::Device>(geom.ProbLo(2));
1193 amrex::Print() <<
"Resetting mesh type to StretchedDz" << std::endl;
1198 #ifdef ERF_USE_TERRAIN_VELOCITY
1199 virtual amrex::Real compute_terrain_velocity (
const double )
1201 amrex::Error(
"Should never call compute_terrain_velocity for "+
name()+
" problem");
1214 amrex::Geometry
const& ,
1215 std::unique_ptr<amrex::MultiFab>& ,
1235 std::string prob_name;
1236 amrex::ParmParse
pp(
"erf");
1237 pp.get(
"prob_name",prob_name);
return prob_name;
1274 const amrex_real*
probhi) AMREX_ATTRIBUTE_WEAK;
@ wbar
Definition: ERF_DataStruct.H:207
auto probhi
Definition: ERF_InitCustomPertVels_ABL.H:37
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
const int nx
Definition: ERF_InitCustomPertVels_CloudChamber.H:14
const int ny
Definition: ERF_InitCustomPertVels_CloudChamber.H:15
Real rho_0
Definition: ERF_InitCustomPert_ABL.H:18
ParmParse pp_prob("prob")
Real T_0
Definition: ERF_InitCustomPert_ABL.H:19
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
const GpuArray< Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_InitCustomPert_CloudChamber.H:33
const int klo
Definition: ERF_InitCustomPert_KE.H:9
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
Dimensionless numeric literals and pure mathematical constants.
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
void init_my_custom_terrain(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
std::unique_ptr< ProblemBase > amrex_probinit(const amrex_real *problo, const amrex_real *probhi) AMREX_ATTRIBUTE_WEAK
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_ProbCommon.H:40
static void make_terrain_surface_periodic(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab)
Definition: ERF_ProbCommon.H:407
void init_terrain_surface(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
Definition: ERF_ProbCommon.H:358
void read_custom_terrain(const std::string &fname, const bool is_usgs, const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &, bool require_domain_coverage=true, bool zero_outside_table=false)
Definition: ERF_ProbCommon.H:934
virtual void update_geostrophic_profile(const double &, amrex::Vector< amrex::Real > &u_geos, amrex::Gpu::DeviceVector< amrex::Real > &d_u_geos, amrex::Vector< amrex::Real > &v_geos, amrex::Gpu::DeviceVector< amrex::Real > &d_v_geos, const amrex::Geometry &geom, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
Definition: ERF_ProbCommon.H:291
virtual void update_rhotheta_sources(const double &, amrex::MultiFab *src, const amrex::Geometry &, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
Definition: ERF_ProbCommon.H:183
amrex::Real phys_zlo(const amrex::GeometryData &geomdata, int lev) const
Physical bottom of the domain at AMR level lev.
Definition: ERF_ProbCommon.H:1255
void set_zlevels_stag(const amrex::Vector< amrex::Vector< amrex::Real >> &zlevels_stag)
Definition: ERF_ProbCommon.H:1246
virtual void erf_init_dens_hse_moist(amrex::MultiFab &, std::unique_ptr< amrex::MultiFab > &, amrex::Geometry const &, const amrex::Vector< amrex::Real > &)
Definition: ERF_ProbCommon.H:95
virtual void erf_init_const_dens_and_linear_th_hse(amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::Real, std::unique_ptr< amrex::MultiFab > &)
Definition: ERF_ProbCommon.H:86
virtual bool terrain_is_specified()
Definition: ERF_ProbCommon.H:480
static bool use_physical_z()
True when heights must come from the vertical levels or z_phys, not from the geometry.
Definition: ERF_ProbCommon.H:1252
virtual void erf_init_const_dens_hse(amrex::MultiFab &)
Definition: ERF_ProbCommon.H:72
virtual void init_custom_terrain(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
Definition: ERF_ProbCommon.H:1174
virtual void erf_init_dens_hse_dry(amrex::MultiFab &, std::unique_ptr< amrex::MultiFab > &, std::unique_ptr< amrex::MultiFab > &, amrex::Geometry const &, const amrex::Vector< amrex::Real > &, bool, bool)
Definition: ERF_ProbCommon.H:57
virtual void update_w_subsidence(const double &, amrex::Vector< amrex::Real > &wbar, amrex::Gpu::DeviceVector< amrex::Real > &d_wbar, const amrex::MultiFab &, const amrex::Geometry &geom, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
Definition: ERF_ProbCommon.H:254
amrex::Vector< amrex::Vector< amrex::Real > > m_zlevels_stag
Staggered vertical levels of AMR level lev (set by set_zlevels_stag)
Definition: ERF_ProbCommon.H:1265
void init_buildings_surface(const amrex::Geometry &geom, amrex::FArrayBox &buildings_fab, const double &time)
Definition: ERF_ProbCommon.H:328
virtual void init_custom_pert(const amrex::Box &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, amrex::GeometryData const &, amrex::Array4< amrex::Real const > const &, const SolverChoice &, const int)
Definition: ERF_ProbCommon.H:121
virtual void erf_init_rayleigh(amrex::Vector< amrex::Vector< amrex::Real > > &, amrex::Geometry const &, std::unique_ptr< amrex::MultiFab > &, amrex::Real)
Definition: ERF_ProbCommon.H:1213
void init_base_parms(amrex::Real rho_0, amrex::Real T_0)
Definition: ERF_ProbCommon.H:1229
virtual ~ProblemBase()=default
amrex::Real phys_zhi(const amrex::GeometryData &geomdata, int lev) const
Physical top of the domain at AMR level lev.
Definition: ERF_ProbCommon.H:1260
std::string name()
Definition: ERF_ProbCommon.H:1234
virtual void erf_init_const_dens_and_th_hse(amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::Real)
Definition: ERF_ProbCommon.H:78
virtual void init_custom_pert_vels(const amrex::Box &, const amrex::Box &, const amrex::Box &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, amrex::GeometryData const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, const SolverChoice &, const int)
Definition: ERF_ProbCommon.H:156
ProbParmDefaults base_parms
Definition: ERF_ProbCommon.H:1223
virtual void update_rhoqt_sources(const double &, amrex::MultiFab *qsrc, const amrex::Geometry &, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
Definition: ERF_ProbCommon.H:217
std::string validate_uniform_axis(const amrex::Vector< amrex::Real > &coordinates, int expected_size, const std::string &axis_name, const std::string &field_description, amrex::Real &origin, amrex::Real &spacing)
Definition: ERF_GridUtils.H:57
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE InterpolationStencil uniform_interpolation_stencil(amrex::Real coordinate, amrex::Real origin, amrex::Real spacing, int point_count) noexcept
Definition: ERF_GridUtils.H:305
const HeightTable & ReadHeightTable(const std::string &fname, bool is_usgs, const GpuArray< Real, AMREX_SPACEDIM > &prob_lo, const GpuArray< Real, AMREX_SPACEDIM > &prob_hi, bool require_domain_coverage)
Definition: ERF_TerrainFileChecks.cpp:331
Definition: ERF_ProbCommon.H:26
amrex::Real T_0
Definition: ERF_ProbCommon.H:28
amrex::Real rho_0
Definition: ERF_ProbCommon.H:27
Definition: ERF_DataStruct.H:715
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:2604
static void set_mesh_type(MeshType new_mesh_type)
Override the globally selected mesh type.
Definition: ERF_DataStruct.H:2611