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>
34 if (std::getline(is, line)) {
35 std::stringstream ss(line);
38 amrex::Abort(
"Failed to read");
41 amrex::Print() <<
"Trying to read line " << n <<
" in this file " << std::endl;
42 amrex::Abort(
"Wrong format: more than one number in this line");
45 amrex::Abort(
"Unable to read this line");
52 amrex::FArrayBox& terrain_fab,
77 std::unique_ptr<amrex::MultiFab>& ,
78 std::unique_ptr<amrex::MultiFab>& ,
79 amrex::Geometry
const& ,
80 const amrex::Vector<amrex::Real>& ,
83 amrex::Print() <<
"Hydrostatically balanced density was NOT set"
84 <<
" -- an appropriate init_type should probably have been specified"
85 <<
" (e.g., input_sounding, WRFInput, or Metgrid)"
87 amrex::Error(
"Should never call this version of erf_init_dens_hse_dry for "+
name()+
" problem");
93 amrex::Error(
"Should never call this version of erf_init_const_dens_hse for "+
name()+
" problem");
98 amrex::MultiFab& , amrex::MultiFab& ,
101 amrex::Error(
"Should never call this version of erf_init_const_dens_and_th_hse for "+
name()+
" problem");
106 amrex::MultiFab& , amrex::MultiFab& ,
108 std::unique_ptr<amrex::MultiFab>& )
110 amrex::Error(
"Should never call this version of erf_init_const_dens_and_linear_th_hse for "+
name()+
" problem");
115 std::unique_ptr<amrex::MultiFab>& ,
116 amrex::Geometry
const& )
138 amrex::Array4<amrex::Real const>
const& ,
139 amrex::Array4<amrex::Real >
const& ,
140 amrex::Array4<amrex::Real >
const& ,
141 amrex::Array4<amrex::Real >
const& ,
142 amrex::Array4<amrex::Real const>
const& ,
143 amrex::Array4<amrex::Real const>
const& ,
144 amrex::GeometryData
const& ,
145 amrex::Array4<amrex::Real const>
const& ,
148 amrex::Print() <<
"No perturbation to background fields supplied for "
149 <<
name() <<
" problem" << std::endl;
172 amrex::Array4<amrex::Real >
const& ,
173 amrex::Array4<amrex::Real >
const& ,
174 amrex::Array4<amrex::Real >
const& ,
175 amrex::Array4<amrex::Real const>
const& ,
176 amrex::GeometryData
const& ,
177 amrex::Array4<amrex::Real const>
const& ,
178 amrex::Array4<amrex::Real const>
const& ,
181 amrex::Print() <<
"No perturbation velocities supplied for " <<
name() <<
" problem" << std::endl;
195 amrex::MultiFab* src,
196 const amrex::Geometry& ,
197 std::unique_ptr<amrex::MultiFab>& )
199 if (src->empty())
return;
201 amrex::Warning(
"Temperature forcing not defined for "+
name()+
" problem");
202 for ( amrex::MFIter mfi(*src, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
204 const auto &box = mfi.tilebox();
205 const amrex::Array4<amrex::Real>& src_arr = src->array(mfi);
208 ParallelFor(box, [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) noexcept {
211 src_arr(i, j, k) =
zero;
227 amrex::MultiFab* qsrc,
228 const amrex::Geometry& ,
229 std::unique_ptr<amrex::MultiFab>& )
231 if (qsrc->empty())
return;
233 amrex::Warning(
"Moisture forcing not defined for "+
name()+
" problem");
234 for ( amrex::MFIter mfi(*qsrc, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
236 const auto &box = mfi.tilebox();
237 const amrex::Array4<amrex::Real>& qsrc_arr = qsrc->array(mfi);
240 ParallelFor(box, [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) noexcept {
243 qsrc_arr(i, j, k) =
zero;
262 amrex::Vector<amrex::Real>&
wbar,
263 amrex::Gpu::DeviceVector<amrex::Real>& d_wbar,
264 const amrex::MultiFab& ,
265 const amrex::Geometry& geom,
266 std::unique_ptr<amrex::MultiFab>& )
268 if (
wbar.empty())
return;
270 amrex::Warning(
"Moisture forcing not defined for "+
name()+
" problem");
272 const int khi = geom.Domain().bigEnd()[2];
275 for (
int k = 0; k <=
khi; k++)
283 amrex::Gpu::copy(amrex::Gpu::hostToDevice,
wbar.begin(),
wbar.end(), d_wbar.begin());
297 amrex::Vector<amrex::Real>& u_geos,
298 amrex::Gpu::DeviceVector<amrex::Real>& d_u_geos,
299 amrex::Vector<amrex::Real>& v_geos,
300 amrex::Gpu::DeviceVector<amrex::Real>& d_v_geos,
301 const amrex::Geometry& geom,
302 std::unique_ptr<amrex::MultiFab>& )
304 if (u_geos.empty())
return;
306 amrex::Warning(
"Geostrophic wind profile not defined for "+
name()+
" problem");
308 const int khi = geom.Domain().bigEnd()[2];
311 for (
int k = 0; k <=
khi; k++)
320 amrex::Gpu::copy(amrex::Gpu::hostToDevice, u_geos.begin(), u_geos.end(), d_u_geos.begin());
321 amrex::Gpu::copy(amrex::Gpu::hostToDevice, v_geos.begin(), v_geos.end(), d_v_geos.begin());
333 amrex::FArrayBox& buildings_fab,
338 amrex::ParmParse
pp(
"erf");
339 auto valid_fname =
pp.queryAdd(
"buildings_file_name",fname);
358 amrex::FArrayBox& terrain_fab,
361 amrex::ParmParse
pp(
"erf");
364 std::string fname_nc;
365 auto valid_fname_nc =
pp.query(
"terrain_file_name_nc", fname_nc);
367 std::string fname, fname_usgs;
368 auto valid_fname =
pp.query(
"terrain_file_name", fname);
369 auto valid_fname_USGS =
pp.query(
"terrain_file_name_USGS", fname_usgs);
371 if (valid_fname_nc) {
372 #ifdef ERF_USE_NETCDF
373 read_terrain_netcdf(fname_nc, geom, terrain_fab, time);
375 amrex::Abort(
"ERF must be compiled with NetCDF support to use terrain_file_name_nc");
377 }
else if (valid_fname) {
380 }
else if (valid_fname_USGS) {
388 #ifdef ERF_USE_NETCDF
390 read_terrain_netcdf (
const std::string& fname,
391 const amrex::Geometry& geom,
392 amrex::FArrayBox& terrain_fab,
395 amrex::Print() <<
"Reading terrain from NetCDF file: " << fname << std::endl;
397 amrex::Vector<amrex::Real> m_xterrain, m_yterrain, m_zterrain;
401 int mass_point_terrain = 0;
403 if (amrex::ParallelDescriptor::IOProcessor()) {
404 auto ncf = ncutils::NCFile::open(fname, NC_NOWRITE);
407 std::string height_varname =
"height";
408 if (!ncf.has_var(
"height")) {
409 if (ncf.has_var(
"z")) height_varname =
"z";
410 else if (ncf.has_var(
"terrain")) height_varname =
"terrain";
411 else if (ncf.has_var(
"HGT_M")) height_varname =
"HGT_M";
412 else if (ncf.has_var(
"AGL")) height_varname =
"AGL";
414 amrex::Abort(
"No terrain height variable found in NetCDF file. "
415 "Expected: 'height', 'z', 'terrain', 'HGT_M', or 'AGL'");
417 amrex::Print() <<
" Using terrain variable: " << height_varname << std::endl;
419 auto var = ncf.var(height_varname);
420 auto dims = var.shape();
421 const auto dim_names = var.dimnames();
422 const auto is_y_dimension = [] (
const std::string&
name) {
423 return name ==
"y" ||
name ==
"south_north";
425 const auto is_x_dimension = [] (
const std::string&
name) {
426 return name ==
"x" ||
name ==
"west_east";
428 const auto is_time_dimension = [] (
const std::string&
name) {
429 return name ==
"time" ||
name ==
"Time";
432 bool has_time_dimension =
false;
433 if (dims.size() == 2) {
434 if (dim_names.size() != 2 || !is_y_dimension(dim_names[0]) ||
435 !is_x_dimension(dim_names[1])) {
436 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
437 fname +
"' must use dimensions (y,x) or "
438 "(south_north,west_east)");
442 }
else if (dims.size() == 3) {
443 if (dim_names.size() != 3 || !is_time_dimension(dim_names[0]) ||
444 !is_y_dimension(dim_names[1]) || !is_x_dimension(dim_names[2])) {
445 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
446 fname +
"' must use dimensions (time,y,x) with time leading; "
447 "accepted layouts are (y,x) and (time,y,x)");
450 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
451 fname +
"' has no time records");
453 has_time_dimension =
true;
457 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" + fname +
458 "' has unsupported rank " + std::to_string(dims.size()) +
459 "; accepted layouts are (y,x) and (time,y,x)");
462 if (nx < 2 || ny < 2) {
463 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" +
464 fname +
"' requires at least two x and y coordinates "
465 "for bilinear interpolation");
468 const bool has_x = ncf.has_var(
"x");
469 const bool has_y = ncf.has_var(
"y");
470 if (has_x != has_y) {
471 amrex::Abort(
"NetCDF terrain file '" + fname +
472 "' must provide both one-dimensional 'x' and 'y' coordinates");
475 const std::string terrain_description =
476 "NetCDF terrain field '" + height_varname +
"' in '" + fname +
"'";
477 m_zterrain.resize(nx * ny);
479 if (height_varname !=
"HGT_M" || !has_time_dimension ||
480 dim_names != std::vector<std::string>{
"Time",
"south_north",
"west_east"}) {
481 amrex::Abort(
"NetCDF terrain field '" + height_varname +
"' in '" + fname +
482 "' requires one-dimensional 'x' and 'y' coordinates; a genuine "
483 "WPS file must provide HGT_M(Time,south_north,west_east)");
486 const auto require_real_attribute = [&] (
const std::string&
name) {
487 if (!ncf.has_attr(
name)) {
488 amrex::Abort(
"WPS terrain file '" + fname +
489 "' is missing required global attribute '" +
name +
"'");
491 std::vector<double> values;
492 ncf.get_attr(
name, values);
493 if (values.size() != 1 || !std::isfinite(values[0])) {
494 amrex::Abort(
"WPS terrain file '" + fname +
495 "' must have one finite global attribute '" +
name +
"'");
499 const auto require_int_attribute = [&] (
const std::string&
name) {
500 if (!ncf.has_attr(
name)) {
501 amrex::Abort(
"WPS terrain file '" + fname +
502 "' is missing required global attribute '" +
name +
"'");
504 std::vector<int> values;
505 ncf.get_attr(
name, values);
506 if (values.size() != 1) {
507 amrex::Abort(
"WPS terrain file '" + fname +
508 "' must have one global attribute '" +
name +
"'");
513 const int map_proj = require_int_attribute(
"MAP_PROJ");
524 if (map_proj != 1 && map_proj != 2 && map_proj != 3) {
527 ?
"; latitude/longitude WPS grids are not supported because their "
528 "DX/DY are in degrees rather than metres"
529 :
"; supported projections are 1 (Lambert), 2 (polar), "
531 amrex::Abort(
"WPS terrain file '" + fname +
"' uses unsupported MAP_PROJ=" +
532 std::to_string(map_proj) + reason);
534 const double cen_lat = require_real_attribute(
"CEN_LAT");
535 const double cen_lon = require_real_attribute(
"CEN_LON");
536 const double stand_lon = require_real_attribute(
"STAND_LON");
537 const double wps_dx = require_real_attribute(
"DX");
538 const double wps_dy = require_real_attribute(
"DY");
539 if (wps_dx <= 0.0 || wps_dy <= 0.0 || std::abs(cen_lat) > 90.0 ||
540 std::abs(cen_lon) > 360.0 || std::abs(stand_lon) > 360.0) {
541 amrex::Abort(
"WPS terrain file '" + fname +
542 "' has invalid projection or grid metadata");
545 const double truelat1 = require_real_attribute(
"TRUELAT1");
546 if (std::abs(truelat1) > 90.0) {
547 amrex::Abort(
"WPS terrain file '" + fname +
548 "' has invalid TRUELAT1");
551 const double truelat2 = require_real_attribute(
"TRUELAT2");
552 if (std::abs(truelat2) > 90.0) {
553 amrex::Abort(
"WPS terrain file '" + fname +
554 "' has invalid TRUELAT2");
558 const int wps_nx = require_int_attribute(
"WEST-EAST_GRID_DIMENSION");
559 const int wps_ny = require_int_attribute(
"SOUTH-NORTH_GRID_DIMENSION");
560 if (wps_nx != nx + 1 || wps_ny != ny + 1) {
561 amrex::Abort(
"WPS terrain file '" + fname +
562 "' has grid dimensions inconsistent with HGT_M: expected " +
563 std::to_string(nx + 1) +
" x " + std::to_string(ny + 1));
566 const auto validate_wps_coordinate = [&] (
const std::string&
name) {
567 if (!ncf.has_var(
name)) {
568 amrex::Abort(
"WPS terrain file '" + fname +
569 "' is missing required coordinate variable '" +
name +
"'");
571 const auto coordinate = ncf.var(
name);
572 if (coordinate.shape() != dims ||
573 coordinate.dimnames() != dim_names) {
574 amrex::Abort(
"WPS terrain coordinate '" +
name +
"' in '" + fname +
575 "' must have dimensions (Time,south_north,west_east) "
578 amrex::Vector<amrex::Real> values(nx * ny);
579 const std::vector<size_t> start{0, 0, 0};
580 const std::vector<size_t> count{
581 1,
static_cast<size_t>(ny),
static_cast<size_t>(nx)};
582 coordinate.get(values.data(), start, count);
583 if (!std::all_of(values.begin(), values.end(),
584 [] (
amrex::Real value) { return std::isfinite(value); })) {
585 amrex::Abort(
"WPS terrain coordinate '" +
name +
"' in '" + fname +
586 "' contains nonfinite values");
589 validate_wps_coordinate(
"XLAT_M");
590 validate_wps_coordinate(
"XLONG_M");
592 const auto dx = geom.CellSizeArray();
593 const auto close_enough = [] (
amrex::Real lhs,
double rhs) {
596 return std::abs(lhs -
static_cast<amrex::Real>(rhs)) <=
599 if (geom.Domain().length(0) != nx || geom.Domain().length(1) != ny ||
600 !close_enough(
dx[0], wps_dx) || !close_enough(
dx[1], wps_dy) ||
601 !close_enough(geom.ProbHi(0) - geom.ProbLo(0), wps_dx * nx) ||
602 !close_enough(geom.ProbHi(1) - geom.ProbLo(1), wps_dy * ny)) {
603 amrex::Abort(
"WPS terrain grid does not match the ERF Geometry; "
604 "HGT_M dimensions, DX/DY, and the ERF domain extent must agree");
612 m_xterrain.resize(nx);
613 m_yterrain.resize(ny);
614 for (
int i = 0; i < nx; ++i) {
615 m_xterrain[i] = geom.ProbLo(0) +
619 for (
int j = 0; j < ny; ++j) {
620 m_yterrain[j] = geom.ProbLo(1) +
624 const std::vector<size_t> start{0, 0, 0};
625 const std::vector<size_t> count{
626 1,
static_cast<size_t>(ny),
static_cast<size_t>(nx)};
627 var.get(m_zterrain.data(), start, count);
628 mass_point_terrain = 1;
630 const auto x_shape = ncf.var(
"x").shape();
631 const auto y_shape = ncf.var(
"y").shape();
632 if (x_shape.size() != 1 || x_shape[0] !=
static_cast<std::size_t
>(nx)) {
633 amrex::Abort(
"NetCDF terrain x coordinate in '" + fname +
634 "' must be one-dimensional with length " + std::to_string(nx));
636 if (y_shape.size() != 1 || y_shape[0] !=
static_cast<std::size_t
>(ny)) {
637 amrex::Abort(
"NetCDF terrain y coordinate in '" + fname +
638 "' must be one-dimensional with length " + std::to_string(ny));
641 m_xterrain.resize(nx);
642 m_yterrain.resize(ny);
643 ncf.var(
"x").get(m_xterrain.data());
644 ncf.var(
"y").get(m_yterrain.data());
646 amrex::Real x_origin, x_spacing, y_origin, y_spacing;
648 m_xterrain, nx,
"x", terrain_description, x_origin, x_spacing);
649 if (coordinate_error.empty()) {
651 m_yterrain, ny,
"y", terrain_description, y_origin, y_spacing);
653 if (!coordinate_error.empty()) {
654 amrex::Abort(coordinate_error);
657 if (has_time_dimension) {
658 const std::vector<size_t> start{0, 0, 0};
659 const std::vector<size_t> count{
660 1,
static_cast<size_t>(ny),
static_cast<size_t>(nx)};
661 var.get(m_zterrain.data(), start, count);
663 var.get(m_zterrain.data());
666 if (!std::all_of(m_zterrain.begin(), m_zterrain.end(),
667 [] (
amrex::Real value) { return std::isfinite(value); })) {
668 amrex::Abort(terrain_description +
" contains nonfinite height values");
672 amrex::Print() <<
" Grid size: " << nx <<
" x " << ny <<
"\n"
673 <<
" X range: [" << m_xterrain[0] <<
", " << m_xterrain[nx-1] <<
"]\n"
674 <<
" Y range: [" << m_yterrain[0] <<
", " << m_yterrain[ny-1] <<
"]\n"
675 <<
" Height range: ["
676 << *std::min_element(m_zterrain.begin(), m_zterrain.end()) <<
", "
677 << *std::max_element(m_zterrain.begin(), m_zterrain.end()) <<
"]\n";
681 amrex::ParallelDescriptor::Bcast(&nx, 1, amrex::ParallelDescriptor::IOProcessorNumber());
682 amrex::ParallelDescriptor::Bcast(&ny, 1, amrex::ParallelDescriptor::IOProcessorNumber());
683 amrex::ParallelDescriptor::Bcast(&mass_point_terrain, 1,
684 amrex::ParallelDescriptor::IOProcessorNumber());
686 if (!amrex::ParallelDescriptor::IOProcessor()) {
687 m_xterrain.resize(nx);
688 m_yterrain.resize(ny);
689 m_zterrain.resize(nx * ny);
692 amrex::ParallelDescriptor::Bcast(m_xterrain.data(), nx, amrex::ParallelDescriptor::IOProcessorNumber());
693 amrex::ParallelDescriptor::Bcast(m_yterrain.data(), ny, amrex::ParallelDescriptor::IOProcessorNumber());
694 amrex::ParallelDescriptor::Bcast(m_zterrain.data(), nx * ny, amrex::ParallelDescriptor::IOProcessorNumber());
697 amrex::Gpu::DeviceVector<amrex::Real> d_xterrain(nx), d_yterrain(ny), d_zterrain(nx * ny);
698 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_xterrain.begin(), m_xterrain.end(), d_xterrain.begin());
699 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_yterrain.begin(), m_yterrain.end(), d_yterrain.begin());
700 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_zterrain.begin(), m_zterrain.end(), d_zterrain.begin());
706 auto dx_arr = geom.CellSizeArray();
707 auto prob_lo = geom.ProbLoArray();
709 int ilo = geom.Domain().smallEnd(0);
710 int jlo = geom.Domain().smallEnd(1);
711 int klo = geom.Domain().smallEnd(2);
712 int ihi = geom.Domain().bigEnd(0) + 1;
713 int jhi = geom.Domain().bigEnd(1) + 1;
715 amrex::Box zbx = terrain_fab.box();
716 amrex::Array4<amrex::Real>
const& z_arr = terrain_fab.array();
717 const amrex::Real terrain_dx = m_xterrain[1] - m_xterrain[0];
718 const amrex::Real terrain_dy = m_yterrain[1] - m_yterrain[0];
720 if (!mass_point_terrain && !zbx.isEmpty()) {
721 amrex::Box target_box = zbx & amrex::convert(geom.Domain(), amrex::IntVect(1, 1, 0));
722 if (!target_box.isEmpty()) {
723 target_box.setSmall(2, zbx.smallEnd(2));
724 target_box.setBig(2, zbx.smallEnd(2));
725 amrex::ReduceOps<amrex::ReduceOpSum, amrex::ReduceOpSum> reduce_op;
726 amrex::ReduceData<amrex::Long, amrex::Long> reduce_data(reduce_op);
727 reduce_op.eval(target_box, reduce_data,
728 [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) -> amrex::GpuTuple<amrex::Long, amrex::Long> {
732 x, d_xt[0], terrain_dx, nx);
734 y, d_yt[0], terrain_dy, ny);
735 const amrex::Long outside =
736 (x_stencil.inside && y_stencil.inside) ? amrex::Long(0) : amrex::Long(1);
737 return {amrex::Long(1), outside};
744 const auto local = reduce_data.value();
745 const amrex::Long total_targets = amrex::get<0>(local);
746 const amrex::Long outside_targets = amrex::get<1>(local);
747 if (amrex::ParallelDescriptor::IOProcessor() && outside_targets > 0) {
757 amrex::Print() <<
"WARNING: Terrain source grid does not cover ERF terrain nodes: outside "
758 << outside_targets <<
" of " << total_targets <<
" ("
759 << percentage <<
"%). Source extent x=[" << m_xterrain[0]
760 <<
", " << source_xhi <<
"] y=[" << m_yterrain[0] <<
", "
761 << source_yhi <<
"]; target extent x=[" << target_xlo <<
", "
762 << target_xhi <<
"] y=[" << target_ylo <<
", " << target_yhi
763 <<
"]. Outside nodes are set to zero.\n";
770 int ii = amrex::min(amrex::max(i, ilo), ihi);
771 int jj = amrex::min(amrex::max(j, jlo), jhi);
778 if (mass_point_terrain) {
783 x = amrex::min(amrex::max(
x, d_xt[0]), d_xt[nx-1]);
784 y = amrex::min(amrex::max(
y, d_yt[0]), d_yt[ny-1]);
787 x, d_xt[0], dx_t, nx);
789 y, d_yt[0], dy_t, ny);
791 if (x_stencil.inside && y_stencil.inside) {
792 const int i_t = x_stencil.lower;
793 const int j_t = y_stencil.lower;
801 amrex::Real z11 = d_zt[(j_t + 1) * nx + (i_t + 1)];
814 amrex::Gpu::synchronize();
821 const amrex::Geometry& geom,
822 amrex::FArrayBox& terrain_fab,
825 amrex::Vector<amrex::Real> m_xterrain,m_yterrain,m_zterrain;
827 int nx = 0;
int ny = 0;
829 if (amrex::ParallelDescriptor::IOProcessor()) {
831 amrex::Print()<<
"Reading terrain file: "<< fname<< std::endl;
832 std::ifstream file(fname);
834 if (!file.is_open()) {
835 amrex::Abort(
"Error: Could not open the file " + fname+
"\n");
839 if (file.peek() == std::ifstream::traits_type::eof()) {
840 amrex::Abort(
"Error: The file " + fname+
" is empty.\n");
848 file >> lon_min >> lat_min;
850 amrex::Error(
"The value of longitude for entry in the first line in " + fname
851 +
" should not exceed amrex::Real(180.) It is " + std::to_string(lon_min));
854 amrex::Error(
"The value of latitude for entry in the first line in " + fname
855 +
" should not exceed amrex::Real(90.) It is " + std::to_string(lat_min));
861 while (file >> value1 >> value2 >> value3) {
862 m_xterrain.push_back(value1);
864 m_yterrain.push_back(value2);
866 m_zterrain.push_back(value3);
869 AMREX_ASSERT(m_xterrain.size() ==
static_cast<long int>(nx*ny));
870 AMREX_ASSERT(m_yterrain.size() ==
static_cast<long int>(ny));
871 AMREX_ASSERT(m_zterrain.size() ==
static_cast<long int>(nx*ny));
875 nx = erf_get_single_value<int>(file,cnt); cnt++;
876 ny = erf_get_single_value<int>(file,cnt); cnt++;
877 amrex::Print()<<
"Expecting " << nx <<
" values of x, " <<
878 ny <<
" values of y, and " <<
879 nx*ny <<
" values of z" << std::endl;
882 m_xterrain.resize(nx);
883 m_yterrain.resize(ny);
884 m_zterrain.resize(nx * ny);
885 for (
int n = 0; n < nx; n++) {
886 m_xterrain[n] = erf_get_single_value<amrex::Real>(file,cnt);
889 for (
int n = 0; n < ny; n++) {
890 m_yterrain[n] = erf_get_single_value<amrex::Real>(file,cnt);
893 for (
int n = 0; n < nx * ny; n++) {
894 m_zterrain[n] = erf_get_single_value<amrex::Real>(file,cnt);
903 amrex::ParallelDescriptor::Bcast(&nx,1,amrex::ParallelDescriptor::IOProcessorNumber());
904 amrex::ParallelDescriptor::Bcast(&ny,1,amrex::ParallelDescriptor::IOProcessorNumber());
910 "USGS terrain file must have at least two points in each direction");
914 int nx_vals = is_usgs ? (nx * ny) : nx;
916 m_xterrain.resize(nx_vals);
917 m_yterrain.resize(ny);
918 m_zterrain.resize(nz);
920 amrex::ParallelDescriptor::Bcast(m_xterrain.data(),nx_vals,amrex::ParallelDescriptor::IOProcessorNumber());
921 amrex::ParallelDescriptor::Bcast(m_yterrain.data(),ny,amrex::ParallelDescriptor::IOProcessorNumber());
922 amrex::ParallelDescriptor::Bcast(m_zterrain.data(),nz,amrex::ParallelDescriptor::IOProcessorNumber());
925 amrex::Gpu::DeviceVector<amrex::Real> d_xterrain(nx_vals),d_yterrain(ny),d_zterrain(nz);
926 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_xterrain.begin(), m_xterrain.end(), d_xterrain.begin());
927 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_yterrain.begin(), m_yterrain.end(), d_yterrain.begin());
928 amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_zterrain.begin(), m_zterrain.end(), d_zterrain.begin());
934 auto dx = geom.CellSizeArray();
935 auto ProbLoArr = geom.ProbLoArray();
937 int ilo = geom.Domain().smallEnd(0);
938 int jlo = geom.Domain().smallEnd(1);
939 int klo = geom.Domain().smallEnd(2);
940 int ihi = geom.Domain().bigEnd(0) + 1;
941 int jhi = geom.Domain().bigEnd(1) + 1;
943 amrex::Box zbx = terrain_fab.box();
944 amrex::Array4<amrex::Real>
const& z_arr = terrain_fab.array();
949 int ii = amrex::min(amrex::max(i,ilo),ihi);
950 int jj = amrex::min(amrex::max(j,jlo),jhi);
956 int ind11, ind12, ind21, ind22;
959 int iindex_terrain=-1;
960 int jindex_terrain=-1;
971 for (
int it = 0; it < ny && !jfound; it++) {
973 jindex_terrain = it-1; jfound =
true;
977 jindex_terrain = ny-1;
980 jindex_terrain = amrex::min(amrex::max(jindex_terrain,0), ny-2);
982 int gstart = (jindex_terrain )*nx;
983 int gend = (jindex_terrain+1)*nx-1;
985 for (
int it = gstart; it <= gend && !ifound; it++) {
987 iindex_terrain = it-gstart-1; ifound =
true;
991 iindex_terrain = nx-1;
994 iindex_terrain = amrex::min(amrex::max(iindex_terrain,0), nx-2);
997 ind11 = jindex_terrain*nx + iindex_terrain;
1004 y1 = d_yt[jindex_terrain];
1005 y2 = d_yt[jindex_terrain+1];
1011 z_arr(i,j,klo) = d_zt[ind11];
1019 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];
1024 bool jfound =
false;
1025 for (
int it = 0; it < ny && !jfound; it++) {
1027 jindex_terrain = it-1; jfound =
true;
1032 jindex_terrain = ny-1;
1034 jindex_terrain = amrex::max(jindex_terrain,0);
1036 bool ifound =
false;
1037 for (
int it = 0; it < nx && !ifound; it++) {
1039 iindex_terrain = it-1; ifound =
true;
1044 iindex_terrain = nx-1;
1046 iindex_terrain = amrex::max(iindex_terrain,0);
1050 int ip1 = amrex::min(iindex_terrain+1,nx-1);
1051 int jp1 = amrex::min(jindex_terrain+1,ny-1);
1054 x1 = d_xt[iindex_terrain];
1056 y1 = d_yt[jindex_terrain];
1063 ind11 = iindex_terrain * ny + jindex_terrain;
1064 ind21 = ip1 * ny + jindex_terrain;
1066 ind12 = iindex_terrain * ny + jp1;
1067 ind22 = ip1 * ny + jp1;
1073 ind11 = jindex_terrain * nx + iindex_terrain;
1074 ind12 = jp1 * nx + iindex_terrain;
1076 ind21 = jindex_terrain * nx + ip1;
1077 ind22 = jp1 * nx + ip1;
1081 bool interp_x = (ip1 != iindex_terrain) && (x2 != x1);
1082 bool interp_y = (jp1 != jindex_terrain) && (y2 != y1);
1084 if (!interp_x && !interp_y)
1086 z_arr(i,j,klo) = d_zt[ind11];
1088 else if (interp_x && !interp_y)
1093 z_arr(i,j,klo) = (w_11*d_zt[ind11] + w_21*d_zt[ind21])/denom;
1095 else if (!interp_x && interp_y)
1100 z_arr(i,j,klo) = (w_11*d_zt[ind11] + w_12*d_zt[ind12])/denom;
1109 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;
1126 amrex::FArrayBox& terrain_fab,
1129 std::string custom_terrain_type =
"None";
1130 amrex::ParmParse
pp_prob(
"prob");
pp_prob.queryAdd(
"custom_terrain_type",custom_terrain_type);
1132 if (custom_terrain_type !=
"None")
1134 amrex::Print() <<
"Calling custom terrain initialization" << std::endl;
1138 amrex::Print() <<
"Initializing flat terrain" << std::endl;
1139 terrain_fab.template setVal<amrex::RunOn::Device>(0);
1143 amrex::Print() <<
"Resetting mesh type to StretchedDz" << std::endl;
1148 #ifdef ERF_USE_TERRAIN_VELOCITY
1149 virtual amrex::Real compute_terrain_velocity(
const double )
1151 amrex::Error(
"Should never call compute_terrain_velocity for "+
name()+
" problem");
1164 amrex::Geometry
const& ,
1165 std::unique_ptr<amrex::MultiFab>& ,
1185 std::string prob_name;
1186 amrex::ParmParse
pp(
"erf");
1187 pp.get(
"prob_name",prob_name);
return prob_name;
1197 const amrex_real*
probhi) AMREX_ATTRIBUTE_WEAK;
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
@ wbar
Definition: ERF_DataStruct.H:153
auto probhi
Definition: ERF_InitCustomPertVels_ABL.H:37
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
Real rho_0
Definition: ERF_InitCustomPert_ABL.H:18
Real T_0
Definition: ERF_InitCustomPert_ABL.H:19
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
ParmParse pp_prob("prob")
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
const GpuArray< Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_InitCustomPert_CloudChamber.H:33
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);})
void init_my_custom_terrain(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
T erf_get_single_value(std::istream &is, int n)
Definition: ERF_ProbCommon.H:30
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:59
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 > &)
Definition: ERF_ProbCommon.H:261
void init_terrain_surface(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
Definition: ERF_ProbCommon.H:357
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 > &)
Definition: ERF_ProbCommon.H:296
virtual void update_rhotheta_sources(const double &, amrex::MultiFab *src, const amrex::Geometry &, std::unique_ptr< amrex::MultiFab > &)
Definition: ERF_ProbCommon.H:194
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:105
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::GeometryData const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, const SolverChoice &, const int)
Definition: ERF_ProbCommon.H:169
virtual void erf_init_const_dens_hse(amrex::MultiFab &)
Definition: ERF_ProbCommon.H:91
virtual void init_custom_terrain(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
Definition: ERF_ProbCommon.H:1125
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:76
virtual void update_rhoqt_sources(const double &, amrex::MultiFab *qsrc, const amrex::Geometry &, std::unique_ptr< amrex::MultiFab > &)
Definition: ERF_ProbCommon.H:226
void init_buildings_surface(const amrex::Geometry &geom, amrex::FArrayBox &buildings_fab, const double &time)
Definition: ERF_ProbCommon.H:332
void read_custom_terrain(const std::string &fname, const bool is_usgs, const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &)
Definition: ERF_ProbCommon.H:819
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:1163
void init_base_parms(amrex::Real rho_0, amrex::Real T_0)
Definition: ERF_ProbCommon.H:1179
virtual ~ProblemBase()=default
virtual void erf_init_dens_hse_moist(amrex::MultiFab &, std::unique_ptr< amrex::MultiFab > &, amrex::Geometry const &)
Definition: ERF_ProbCommon.H:114
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::GeometryData const &, amrex::Array4< amrex::Real const > const &, const SolverChoice &, const int)
Definition: ERF_ProbCommon.H:137
std::string name()
Definition: ERF_ProbCommon.H:1184
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:97
ProbParmDefaults base_parms
Definition: ERF_ProbCommon.H:1173
@ T
Definition: ERF_IndexDefines.H:128
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:56
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:173
Definition: ERF_ProbCommon.H:24
amrex::Real T_0
Definition: ERF_ProbCommon.H:26
amrex::Real rho_0
Definition: ERF_ProbCommon.H:25
Definition: ERF_DataStruct.H:634
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:1848
static void set_mesh_type(MeshType new_mesh_type)
Override the globally selected mesh type.
Definition: ERF_DataStruct.H:1855