ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF Class Reference

#include <ERF.H>

Inheritance diagram for ERF:
Collaboration diagram for ERF:

Public Member Functions

 ERF ()
 
 ~ERF () override
 
void ERF_shared ()
 
 ERF (ERF &&) noexcept=delete
 
ERFoperator= (ERF &&other) noexcept=delete
 
 ERF (const ERF &other)=delete
 
ERFoperator= (const ERF &other)=delete
 
void Evolve ()
 
amrex::Real EvolveOneStep (amrex::Real time, amrex::Real dt_request)
 
void PackAtmosphericStates (amrex::Vector< amrex::MultiFab * > &states, amrex::Real time)
 
void ApplyOceanSurfaceState (const amrex::Vector< amrex::MultiFab * > &state, amrex::Real time)
 
void ErrorEst (int lev, amrex::TagBoxArray &tags, amrex::Real time, int ngrow) override
 
void read_box_for_refinement (std::string &ref_prefix, int &lev_for_box, amrex::RealBox &real_box)
 
void update_box_for_refinement (std::string &ref_prefix, int &lev_for_box, amrex::RealBox &real_box, const amrex::Real time)
 
void HurricaneTracker (int lev, amrex::Real time, const amrex::MultiFab &cc_vel, const amrex::Real velmag_threshold, amrex::TagBoxArray *tags=nullptr)
 
bool FindInitialEye (int lev, const amrex::MultiFab &cc_vel, const amrex::Real velmag_threshold, amrex::Real &eye_x, amrex::Real &eye_y)
 
std::string MakeVTKFilename (int nstep)
 
std::string MakeVTKFilename_TrackerCircle (int nstep)
 
std::string MakeVTKFilename_EyeTracker_xy (int nstep)
 
std::string MakeFilename_EyeTracker_latlon (int nstep)
 
std::string MakeFilename_EyeTracker_maxvel (int nstep)
 
std::string MakeFilename_EyeTracker_minpressure (int nstep)
 
void WriteVTKPolyline (const std::string &filename, amrex::Vector< std::array< amrex::Real, 2 >> &points_xy)
 
void WriteLinePlot (const std::string &filename, amrex::Vector< std::array< amrex::Real, 2 >> &points_xy)
 
void HurricaneEyeTracker_WRF (const SolverChoice &solverChoice)
 
void HurricaneEyeTrackerInitial_WRF (const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, const amrex::Real &hurricane_eye_latitude, const amrex::Real &hurricane_eye_longitude)
 
void HurricaneEyeTrackerNotInitial_WRF (const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, MoistureType moisture_type)
 
void ComputeGlobalMinLocation_WRF (const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, amrex::Real *d_val_min_ptr, int *d_i_min_ptr, int *d_j_min_ptr, amrex::Real &global_val_min, int &global_i_min, int &global_j_min)
 
void HurricaneTrackerCircle_WRF ()
 
void ReadStormTrackerRestart ()
 
void InitData ()
 
void InitData_pre ()
 
void InitData_post ()
 
void Interp2DArrays (int lev, const amrex::BoxArray &my_ba2d, const amrex::DistributionMapping &my_dm)
 
void WriteMyEBSurface ()
 
void compute_divergence (int lev, amrex::MultiFab &rhs, amrex::Array< amrex::MultiFab const *, AMREX_SPACEDIM > rho0_u_const, amrex::Geometry const &geom_at_lev)
 
void project_initial_velocity (int lev, amrex::Real time, amrex::Real dt)
 
void project_momenta (int lev, amrex::Real l_time, amrex::Real l_dt, amrex::Vector< amrex::MultiFab > &vars)
 
void project_velocity_tb (int lev, amrex::Real dt, amrex::Vector< amrex::MultiFab > &vars)
 
void poisson_wall_dist (int lev)
 
void make_subdomains (const amrex::BoxList &ba, amrex::Vector< amrex::BoxArray > &bins)
 
void solve_with_gmres (int lev, const amrex::Box &subdomain, amrex::MultiFab &rhs, amrex::MultiFab &p, amrex::Array< amrex::MultiFab, AMREX_SPACEDIM > &fluxes, amrex::MultiFab &ax_sub, amrex::MultiFab &ay_sub, amrex::MultiFab &az_sub, amrex::MultiFab &, amrex::MultiFab &znd_sub)
 
void ImposeBCsOnPhi (int lev, amrex::MultiFab &phi, const amrex::Box &subdomain)
 
void init_only (int lev, amrex::Real time)
 
void restart ()
 
void check_state_for_nans (amrex::MultiFab const &S)
 
void check_vels_for_nans (amrex::MultiFab const &xvel, amrex::MultiFab const &yvel, amrex::MultiFab const &zvel)
 
void check_for_negative_theta (amrex::MultiFab &S)
 
void check_for_low_temp (amrex::MultiFab &S)
 
void compute_max_pressure_gradient_diagnostic (int lev)
 
void check_mesh_type (int lev)
 
bool writeNow (double cur_time, const int nstep, const int plot_int, const amrex::Real plot_per, const amrex::Real dt_0, amrex::Real &last_file_time)
 
void post_timestep (int nstep, double time, amrex::Real dt_lev)
 
void WriteAtIntermediateTime (int nstep, double time)
 
void WriteAtFinalTime ()
 
void sum_integrated_quantities (double time)
 
void sum_derived_quantities (double time)
 
void sum_energy_quantities (double time)
 
void write_1D_profiles (double time)
 
void write_1D_profiles_stag (double time)
 
amrex::Real cloud_fraction (double time)
 
void FillBdyCCVels (amrex::MultiFab &mf_cc_vel, amrex::Geometry &lev_geom)
 
void sample_points (int lev, amrex::Real time, amrex::IntVect cell, amrex::MultiFab &mf)
 
void sample_lines (int lev, amrex::Real time, amrex::IntVect cell, amrex::MultiFab &mf)
 
void derive_diag_profiles (double time, amrex::Gpu::HostVector< amrex::Real > &h_avg_u, amrex::Gpu::HostVector< amrex::Real > &h_avg_v, amrex::Gpu::HostVector< amrex::Real > &h_avg_w, amrex::Gpu::HostVector< amrex::Real > &h_avg_rho, amrex::Gpu::HostVector< amrex::Real > &h_avg_th, amrex::Gpu::HostVector< amrex::Real > &h_avg_ksgs, amrex::Gpu::HostVector< amrex::Real > &h_avg_Kmv, amrex::Gpu::HostVector< amrex::Real > &h_avg_Khv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qc, amrex::Gpu::HostVector< amrex::Real > &h_avg_qr, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqv, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqc, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqr, amrex::Gpu::HostVector< amrex::Real > &h_avg_qi, amrex::Gpu::HostVector< amrex::Real > &h_avg_qs, amrex::Gpu::HostVector< amrex::Real > &h_avg_qg, amrex::Gpu::HostVector< amrex::Real > &h_avg_uu, amrex::Gpu::HostVector< amrex::Real > &h_avg_uv, amrex::Gpu::HostVector< amrex::Real > &h_avg_uw, amrex::Gpu::HostVector< amrex::Real > &h_avg_vv, amrex::Gpu::HostVector< amrex::Real > &h_avg_vw, amrex::Gpu::HostVector< amrex::Real > &h_avg_ww, amrex::Gpu::HostVector< amrex::Real > &h_avg_uth, amrex::Gpu::HostVector< amrex::Real > &h_avg_vth, amrex::Gpu::HostVector< amrex::Real > &h_avg_wth, amrex::Gpu::HostVector< amrex::Real > &h_avg_thth, amrex::Gpu::HostVector< amrex::Real > &h_avg_ku, amrex::Gpu::HostVector< amrex::Real > &h_avg_kv, amrex::Gpu::HostVector< amrex::Real > &h_avg_kw, amrex::Gpu::HostVector< amrex::Real > &h_avg_p, amrex::Gpu::HostVector< amrex::Real > &h_avg_pu, amrex::Gpu::HostVector< amrex::Real > &h_avg_pv, amrex::Gpu::HostVector< amrex::Real > &h_avg_pw, amrex::Gpu::HostVector< amrex::Real > &h_avg_wthv)
 
void derive_diag_profiles_stag (double time, amrex::Gpu::HostVector< amrex::Real > &h_avg_u, amrex::Gpu::HostVector< amrex::Real > &h_avg_v, amrex::Gpu::HostVector< amrex::Real > &h_avg_w, amrex::Gpu::HostVector< amrex::Real > &h_avg_rho, amrex::Gpu::HostVector< amrex::Real > &h_avg_th, amrex::Gpu::HostVector< amrex::Real > &h_avg_ksgs, amrex::Gpu::HostVector< amrex::Real > &h_avg_Kmv, amrex::Gpu::HostVector< amrex::Real > &h_avg_Khv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qc, amrex::Gpu::HostVector< amrex::Real > &h_avg_qr, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqv, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqc, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqr, amrex::Gpu::HostVector< amrex::Real > &h_avg_qi, amrex::Gpu::HostVector< amrex::Real > &h_avg_qs, amrex::Gpu::HostVector< amrex::Real > &h_avg_qg, amrex::Gpu::HostVector< amrex::Real > &h_avg_uu, amrex::Gpu::HostVector< amrex::Real > &h_avg_uv, amrex::Gpu::HostVector< amrex::Real > &h_avg_uw, amrex::Gpu::HostVector< amrex::Real > &h_avg_vv, amrex::Gpu::HostVector< amrex::Real > &h_avg_vw, amrex::Gpu::HostVector< amrex::Real > &h_avg_ww, amrex::Gpu::HostVector< amrex::Real > &h_avg_uth, amrex::Gpu::HostVector< amrex::Real > &h_avg_vth, amrex::Gpu::HostVector< amrex::Real > &h_avg_wth, amrex::Gpu::HostVector< amrex::Real > &h_avg_thth, amrex::Gpu::HostVector< amrex::Real > &h_avg_ku, amrex::Gpu::HostVector< amrex::Real > &h_avg_kv, amrex::Gpu::HostVector< amrex::Real > &h_avg_kw, amrex::Gpu::HostVector< amrex::Real > &h_avg_p, amrex::Gpu::HostVector< amrex::Real > &h_avg_pu, amrex::Gpu::HostVector< amrex::Real > &h_avg_pv, amrex::Gpu::HostVector< amrex::Real > &h_avg_pw, amrex::Gpu::HostVector< amrex::Real > &h_avg_wthv)
 
void derive_stress_profiles (amrex::Gpu::HostVector< amrex::Real > &h_avg_tau11, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau12, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau13, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau22, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau23, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau33, amrex::Gpu::HostVector< amrex::Real > &h_avg_hfx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q1fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q2fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_diss)
 
void derive_stress_profiles_stag (amrex::Gpu::HostVector< amrex::Real > &h_avg_tau11, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau12, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau13, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau22, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau23, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau33, amrex::Gpu::HostVector< amrex::Real > &h_avg_hfx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q1fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q2fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_diss)
 
amrex::Real volWgtSumMF (int lev, const amrex::MultiFab &mf, int comp, const amrex::MultiFab &dJ, const amrex::MultiFab &mfx, const amrex::MultiFab &mfy, bool finemask, bool local=true)
 
void volWgtColumnSum (int lev, const amrex::MultiFab &mf, int comp, amrex::MultiFab &mf_2d, const amrex::MultiFab &dJ)
 
void MakeNewLevelFromCoarse (int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
 
void RemakeLevel (int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
 
void ClearLevel (int lev) override
 
void MakeNewLevelFromScratch (int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
 
amrex::Real estTimeStep (int lev, long &dt_fast_ratio) const
 
void advance_dycore (int level, amrex::Vector< amrex::MultiFab > &state_old, amrex::Vector< amrex::MultiFab > &state_new, amrex::MultiFab &xvel_old, amrex::MultiFab &yvel_old, amrex::MultiFab &zvel_old, amrex::MultiFab &xvel_new, amrex::MultiFab &yvel_new, amrex::MultiFab &zvel_new, amrex::MultiFab &source, amrex::MultiFab &xmom_src, amrex::MultiFab &ymom_src, amrex::MultiFab &zmom_src, amrex::MultiFab &buoyancy, amrex::Geometry fine_geom, amrex::Real dt, amrex::Real time)
 
void advance_microphysics (int lev, amrex::MultiFab &cons_in, const amrex::Real &dt_advance, const int &iteration, const amrex::Real &time)
 
void advance_lsm (int lev, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, const amrex::Real &time, const amrex::Real &dt_advance)
 
void advance_radiation (int lev, amrex::MultiFab &cons_in, const amrex::Real &dt_advance)
 
void build_fine_mask (int lev, amrex::MultiFab &fine_mask)
 
void MakeHorizontalAverages ()
 
void MakeDiagnosticAverage (amrex::Vector< amrex::Real > &h_havg, amrex::MultiFab &S, int n)
 
void derive_upwp (amrex::Vector< amrex::Real > &h_havg)
 
void Write3DPlotFile (int which, PlotFileType plotfile_type, amrex::Vector< std::string > plot_var_names)
 
void Write2DPlotFile (int which, PlotFileType plotfile_type, amrex::Vector< std::string > plot_var_names)
 
void WriteSubvolume (int isub, amrex::Vector< std::string > subvol_var_names)
 
void WriteMultiLevelPlotfileWithTerrain (const std::string &plotfilename, int nlevels, const amrex::Vector< const amrex::MultiFab * > &mf, const amrex::Vector< const amrex::MultiFab * > &mf_nd, const amrex::Vector< std::string > &varnames, const amrex::Vector< amrex::Geometry > &my_geom, amrex::Real time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &my_ref_ratio, const std::string &versionName="HyperCLaw-V1.1", const std::string &levelPrefix="Level_", const std::string &mfPrefix="Cell", const amrex::Vector< std::string > &extra_dirs=amrex::Vector< std::string >()) const
 
void WriteGenericPlotfileHeaderWithTerrain (std::ostream &HeaderFile, int nlevels, const amrex::Vector< amrex::BoxArray > &bArray, const amrex::Vector< std::string > &varnames, const amrex::Vector< amrex::Geometry > &my_geom, amrex::Real time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &my_ref_ratio, const std::string &versionName, const std::string &levelPrefix, const std::string &mfPrefix) const
 
void erf_enforce_hse (int lev, amrex::MultiFab &dens, amrex::MultiFab &pres, amrex::MultiFab &pi, amrex::MultiFab &th, amrex::MultiFab &qv, std::unique_ptr< amrex::MultiFab > &z_cc)
 
void init_from_input_sounding (int lev)
 
void init_immersed_forcing (int lev)
 
void input_sponge (int lev)
 
void init_from_hse (int lev)
 
void init_thin_body (int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
 
void FillForecastStateMultiFabs (const int lev, const std::string &filename, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, amrex::Vector< amrex::Vector< amrex::MultiFab >> &forecast_state)
 
void FillSurfaceStateMultiFabs (const int lev, const std::string &filename, amrex::Vector< amrex::MultiFab > &surface_state)
 
void WeatherDataInterpolation (const int nlevs, const amrex::Real time, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, bool regrid_forces_file_read)
 
void SurfaceDataInterpolation (const int nlevs, const amrex::Real time, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, bool regrid_forces_file_read)
 
void create_background_state_for_ensemble (int lev, amrex::MultiFab &mf_cc_pert, amrex::MultiFab &cons_pert, amrex::MultiFab &xvel_pert, amrex::MultiFab &yvel_pert, amrex::MultiFab &zvel_pert)
 
void create_random_perturbations (const int lev, amrex::MultiFab &mf_cc_pert)
 
void apply_gaussian_smoothing_to_perturbations (const int lev, amrex::MultiFab &mf_cc_pert)
 
void ComputeAndWriteEnsemblePerturbations ()
 
void PerformDataAssimilation (int da_iter)
 
void init_custom (int lev)
 
void fill_from_bndryregs (const amrex::Vector< amrex::MultiFab * > &mfs, amrex::Real time)
 
void MakeEBGeometry ()
 
void make_eb_box ()
 
void make_eb_regular ()
 
void AverageDownTo (int crse_lev, int scomp, int ncomp, bool do_perturbational_and_momenta=true)
 
void AverageDownMoistStateTo (int crse_lev)
 Conservation-preserving fine→coarse average of moist state components (RhoTheta and the contiguous moist q range) using detJ/mfac weighting, reusing AverageDownTo with perturbational/momenta handling disabled. More...
 
void WriteCheckpointFile () const
 
void ReadCheckpointFile ()
 
void ReadVelsOnlyFromCheckpointFile (int lev_to_fill, std::string &chkfile)
 
void ReadCheckpointFileSurfaceLayer ()
 
void init_zphys (int lev, amrex::Real elapsed_time)
 
void remake_zphys (int lev, std::unique_ptr< amrex::MultiFab > &temp_zphys_nd)
 
void update_terrain_arrays (int lev)
 
void writeJobInfo (const std::string &dir) const
 

Static Public Member Functions

static bool is_it_time_for_action (int nstep, double time, amrex::Real dt, int action_interval, amrex::Real action_per)
 
static void writeBuildInfo (std::ostream &os)
 
static void print_banner (MPI_Comm, std::ostream &)
 
static void print_usage (MPI_Comm, std::ostream &)
 
static void print_error (MPI_Comm, const std::string &msg)
 
static void print_summary (std::ostream &)
 
static void print_tpls (std::ostream &)
 

Public Attributes

amrex::Vector< std::array< amrex::Real, 2 > > hurricane_track_xy
 
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_eye_track_xy
 
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_eye_track_latlon
 
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_maxvel_vs_time
 
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_minpressure_vs_time
 
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_tracker_circle
 
amrex::Vector< amrex::MultiFab > weather_forecast_data_1
 
amrex::Vector< amrex::MultiFab > weather_forecast_data_2
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_1
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_2
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_interp
 
amrex::Vector< amrex::MultiFab > surface_state_1
 
amrex::Vector< amrex::MultiFab > surface_state_2
 
amrex::Vector< amrex::MultiFab > surface_state_interp
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > bckgnd_state
 
std::string pp_prefix {"erf"}
 

Private Member Functions

void ReadParameters ()
 
void ParameterSanityChecks ()
 
void AverageDown ()
 
void update_diffusive_arrays (int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
 
void Construct_ERFFillPatchers (int lev)
 
void Define_ERFFillPatchers (int lev)
 
void init1DArrays ()
 
void init_bcs ()
 
void init_phys_bcs (bool &rho_read, bool &read_prim_theta)
 
void init_stuff (int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm, amrex::Vector< amrex::MultiFab > &lev_new, amrex::Vector< amrex::MultiFab > &lev_old, amrex::MultiFab &tmp_base_state, std::unique_ptr< amrex::MultiFab > &tmp_zphys_nd)
 
void turbPert_update (const int lev, const amrex::Real dt)
 
void turbPert_amplitude (const int lev)
 
void initialize_integrator (int lev, amrex::MultiFab &cons_mf, amrex::MultiFab &vel_mf)
 
void make_physbcs (int lev)
 
void initializeMicrophysics (const int &)
 
void FillPatchCrseLevel (int lev, double time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, bool cons_only=false)
 
void FillPatchFineLevel (int lev, double time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, const amrex::Vector< amrex::MultiFab * > &mfs_mom, const amrex::MultiFab &old_base_state, const amrex::MultiFab &new_base_state, bool fillset=true, bool cons_only=false)
 
void FillIntermediatePatch (int lev, amrex::Real time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, const amrex::Vector< amrex::MultiFab * > &mfs_mom, int ng_cons, int ng_vel, bool cons_only, int icomp_cons, int ncomp_cons)
 
void FillCoarsePatch (int lev, amrex::Real time)
 
void timeStep (int lev, double time, int iteration)
 
void Advance (int lev, amrex::Real time, amrex::Real dt_lev, int iteration, int ncycle)
 
void initHSE ()
 Initialize HSE. More...
 
void initHSE (int lev)
 
void initRayleigh_at_level (const int &lev)
 Initialize Rayleigh damping profiles at a level. More...
 
void initSponge ()
 Initialize sponge profiles. More...
 
void setRayleighRefFromSounding (bool restarting)
 Set Rayleigh mean profiles from input sounding. More...
 
void setSpongeRefFromSounding (bool restarting)
 Set sponge mean profiles from input sounding. More...
 
void ComputeDt (int step=-1, double cur_time_d=0.0)
 
std::string PlotFileName (int lev) const
 
void setPlotVariables (const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
 
void setPlotVariables2D (const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
 
void appendPlotVariables (const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
 
void setSubVolVariables (const std::string &pp_subvol_var_names, amrex::Vector< std::string > &subvol_var_names)
 
void init_Dirichlet_bc_data (const std::string input_file)
 
void InitializeFromFile ()
 
void InitializeLevelFromData (int lev, const amrex::MultiFab &initial_data)
 
void post_update (amrex::MultiFab &state_mf, amrex::Real time, const amrex::Geometry &geom)
 
void fill_rhs (amrex::MultiFab &rhs_mf, const amrex::MultiFab &state_mf, amrex::Real time, const amrex::Geometry &geom)
 
void init_geo_wind_profile (const std::string input_file, amrex::Vector< amrex::Real > &u_geos, amrex::Gpu::DeviceVector< amrex::Real > &u_geos_d, amrex::Vector< amrex::Real > &v_geos, amrex::Gpu::DeviceVector< amrex::Real > &v_geos_d, const amrex::Geometry &lgeom, const amrex::Vector< amrex::Real > &zlev_stag)
 
void refinement_criteria_setup ()
 
AMREX_FORCE_INLINE amrex::YAFluxRegister * getAdvFluxReg (int lev)
 
AMREX_FORCE_INLINE std::ostream & DataLog (int i)
 
AMREX_FORCE_INLINE std::ostream & DerDataLog (int i)
 
AMREX_FORCE_INLINE int NumDataLogs () noexcept
 
AMREX_FORCE_INLINE int NumDerDataLogs () noexcept
 
AMREX_FORCE_INLINE std::ostream & SamplePointLog (int i)
 
AMREX_FORCE_INLINE int NumSamplePointLogs () noexcept
 
AMREX_FORCE_INLINE std::ostream & SampleLineLog (int i)
 
AMREX_FORCE_INLINE int NumSampleLineLogs () noexcept
 
amrex::IntVect & SamplePoint (int i)
 
AMREX_FORCE_INLINE int NumSamplePoints () noexcept
 
amrex::IntVect & SampleLine (int i)
 
AMREX_FORCE_INLINE int NumSampleLines () noexcept
 
void setRecordDataInfo (int i, const std::string &filename)
 
void setRecordDerDataInfo (int i, const std::string &filename)
 
void setRecordEnergyDataInfo (int i, const std::string &filename)
 
void setRecordSamplePointInfo (int i, int lev, amrex::IntVect &cell, const std::string &filename)
 
void setRecordSampleLineInfo (int i, int lev, amrex::IntVect &cell, const std::string &filename)
 
std::string DataLogName (int i) const noexcept
 The filename of the ith datalog file. More...
 
std::string DerDataLogName (int i) const noexcept
 
std::string SamplePointLogName (int i) const noexcept
 The filename of the ith sampleptlog file. More...
 
std::string SampleLineLogName (int i) const noexcept
 The filename of the ith samplelinelog file. More...
 
eb_ const & get_eb (int lev) const noexcept
 
amrex::EBFArrayBoxFactory const & EBFactory (int lev) const noexcept
 

Static Private Member Functions

static amrex::Vector< std::string > PlotFileVarNames (amrex::Vector< std::string > plot_var_names)
 
static void GotoNextLine (std::istream &is)
 
static AMREX_FORCE_INLINE int ComputeGhostCells (const SolverChoice &sc)
 
static amrex::Real getCPUTime ()
 
static int nghost_eb_basic ()
 
static int nghost_eb_volume ()
 
static int nghost_eb_full ()
 

Private Attributes

amrex::Vector< std::unique_ptr< amrex::MultiFab > > lat_m
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lon_m
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > sinPhi_m
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > cosPhi_m
 
InputSoundingData input_sounding_data
 
InputSpongeData input_sponge_data
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > xvel_bc_data
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > yvel_bc_data
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > zvel_bc_data
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > th_bc_data
 
std::unique_ptr< ProblemBaseprob = nullptr
 
amrex::Vector< int > num_boxes_at_level
 
amrex::Vector< int > num_files_at_level
 
amrex::Vector< amrex::Vector< amrex::Box > > boxes_at_level
 
amrex::Vector< int > istep
 
amrex::Vector< int > nsubsteps
 
amrex::Vector< amrex::Realt_new
 
amrex::Vector< amrex::Realt_old
 
amrex::Vector< amrex::Realdt
 
amrex::Vector< long > dt_mri_ratio
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > vars_new
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > vars_old
 
amrex::Vector< amrex::Vector< amrex::MultiFab > > gradp
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vel_t_avg
 
amrex::Vector< amrex::Realt_avg_cnt
 
amrex::Vector< std::unique_ptr< MRISplitIntegrator< amrex::Vector< amrex::MultiFab > > > > mri_integrator_mem
 
amrex::Vector< amrex::MultiFab > pp_inc
 
amrex::Vector< amrex::MultiFab > lagged_delta_rt
 
amrex::Vector< amrex::MultiFab > avg_xmom
 
amrex::Vector< amrex::MultiFab > avg_ymom
 
amrex::Vector< amrex::MultiFab > avg_zmom
 
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_cons > > physbcs_cons
 
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_u > > physbcs_u
 
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_v > > physbcs_v
 
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_w > > physbcs_w
 
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_base > > physbcs_base
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Theta_prim
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Qv_prim
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Qr_prim
 
amrex::Vector< amrex::MultiFab > rU_old
 
amrex::Vector< amrex::MultiFab > rU_new
 
amrex::Vector< amrex::MultiFab > rV_old
 
amrex::Vector< amrex::MultiFab > rV_new
 
amrex::Vector< amrex::MultiFab > rW_old
 
amrex::Vector< amrex::MultiFab > rW_new
 
amrex::Vector< amrex::MultiFab > zmom_crse_rhs
 
std::unique_ptr< Microphysicsmicro
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > qmoist
 
LandSurface lsm
 
amrex::Vector< std::string > lsm_data_name
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_data
 
amrex::Vector< std::string > lsm_flux_name
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_flux
 
amrex::Vector< std::unique_ptr< IRadiation > > rad
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > qheating_rates
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rad_fluxes
 
bool plot_rad = false
 
int rad_datalog_int = -1
 
int cf_width {0}
 
int cf_set_width {0}
 
amrex::Vector< ERFFillPatcherFPr_c
 
amrex::Vector< ERFFillPatcherFPr_u
 
amrex::Vector< ERFFillPatcherFPr_v
 
amrex::Vector< ERFFillPatcherFPr_w
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > Tau
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > Tau_corr
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > eddyDiffs_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SmnSmn_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > sst_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > tsk_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > lmask_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > land_type_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > soil_type_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > urb_frac_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx1_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx2_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx3_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_diss_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx1_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx2_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx3_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q2fx3_lev
 
amrex::Vector< amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > > Tau_EB
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > hfx3_EB
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > qfx3_EB
 
amrex::Vector< amrex::Vector< amrex::Real > > zlevels_stag
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_cc
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ax
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ay
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > az
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_cc_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ax_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ay_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > az_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd_new
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc_new
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_t_rk
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > walldist
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > mapfac
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > fine_mask
 
amrex::Vector< amrex::Vector< amrex::Real > > stretched_dz_h
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > stretched_dz_d
 
amrex::Vector< amrex::MultiFab > base_state
 
amrex::Vector< amrex::MultiFab > base_state_new
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Hwave
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Lwave
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Hwave_onegrid
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Lwave_onegrid
 
bool finished_wave = false
 
amrex::Vector< amrex::YAFluxRegister * > advflux_reg
 
amrex::Vector< amrex::BCRec > domain_bcs_type
 
amrex::Gpu::DeviceVector< amrex::BCRec > domain_bcs_type_d
 
amrex::Array< std::string, 2 *AMREX_SPACEDIM > domain_bc_type
 
amrex::Array< amrex::Array< amrex::Real, AMREX_SPACEDIM *2 >, AMREX_SPACEDIM+NBCVAR_maxm_bc_extdir_vals
 
amrex::Array< amrex::Array< amrex::Real, AMREX_SPACEDIM *2 >, AMREX_SPACEDIM+NBCVAR_maxm_bc_neumann_vals
 
amrex::Array< bool, AMREX_SPACEDIM *2 > m_bc_nonreflecting = {{false}}
 
amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > phys_bc_type
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > xflux_imask
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > yflux_imask
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > zflux_imask
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_xforce
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_yforce
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_zforce
 
amrex::Vector< int > last_subvol_step
 
amrex::Vector< amrex::Reallast_subvol_time
 
const int datwidth = 14
 
const int datprecision = 6
 
const int timeprecision = 13
 
int max_step = -1
 
bool use_datetime = false
 
const std::string datetime_format = "%Y-%m-%d %H:%M:%S"
 
std::string restart_chkfile = ""
 
amrex::Vector< amrex::Realfixed_dt
 
amrex::Vector< amrex::Realfixed_fast_dt
 
int regrid_int = -1
 
bool regrid_level_0_on_restart = false
 
std::string plot3d_file_1 {"plt_1_"}
 
std::string plot3d_file_2 {"plt_2_"}
 
std::string plot2d_file_1 {"plt2d_1_"}
 
std::string plot2d_file_2 {"plt2d_2_"}
 
std::string subvol_file {"subvol"}
 
bool m_expand_plotvars_to_unif_rr = false
 
int m_plot3d_int_1 = -1
 
int m_plot3d_int_2 = -1
 
int m_plot2d_int_1 = -1
 
int m_plot2d_int_2 = -1
 
amrex::Vector< int > m_subvol_int
 
amrex::Vector< amrex::Realm_subvol_per
 
amrex::Real m_plot3d_per_1 = -one
 
amrex::Real m_plot3d_per_2 = -one
 
amrex::Real m_plot2d_per_1 = -one
 
amrex::Real m_plot2d_per_2 = -one
 
bool m_plot_face_vels = false
 
bool plot_lsm = false
 
int profile_int = -1
 
bool destag_profiles = true
 
std::string check_file {"chk"}
 
int m_check_int = -1
 
amrex::Real m_check_per = -one
 
amrex::Vector< std::string > subvol3d_var_names
 
amrex::Vector< std::string > plot3d_var_names_1
 
amrex::Vector< std::string > plot3d_var_names_2
 
amrex::Vector< std::string > plot2d_var_names_1
 
amrex::Vector< std::string > plot2d_var_names_2
 
const amrex::Vector< std::string > cons_names
 
const amrex::Vector< std::string > derived_names
 
const amrex::Vector< std::string > derived_subvol_names {"soundspeed", "temp", "theta", "KE", "scalar"}
 
TurbulentPerturbation turbPert
 
int file_name_digits = 5
 
bool use_real_time_in_pltname = false
 
int real_width {0}
 
bool real_extrap_w {true}
 
bool metgrid_debug_quiescent {false}
 
bool metgrid_debug_isothermal {false}
 
bool metgrid_debug_dry {false}
 
bool metgrid_debug_psfc {false}
 
bool metgrid_debug_msf {false}
 
bool metgrid_interp_theta {false}
 
bool metgrid_basic_linear {false}
 
bool metgrid_use_below_sfc {true}
 
bool metgrid_use_sfc {true}
 
bool metgrid_retain_sfc {false}
 
amrex::Real metgrid_proximity {amrex::Real(500.0)}
 
int metgrid_order {2}
 
int metgrid_force_sfc_k {6}
 
bool write_erfbdy {false}
 
bool use_erfbdy {false}
 
std::string erfbdy_file {"erfbdy"}
 
int nvars_erfbdy {0}
 
amrex::Vector< amrex::BoxArray > ba1d
 
amrex::Vector< amrex::BoxArray > ba2d
 
std::unique_ptr< amrex::MultiFab > wrf_C1H
 
std::unique_ptr< amrex::MultiFab > wrf_C2H
 
std::unique_ptr< amrex::MultiFab > wrf_MUB
 
std::unique_ptr< amrex::MultiFab > wrf_PHB
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > mf_PSFC
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rhotheta_src
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rhoqt_src
 
amrex::Vector< amrex::Vector< amrex::Real > > h_w_subsid
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_w_subsid
 
amrex::Vector< amrex::Vector< amrex::Real > > h_u_geos
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_u_geos
 
amrex::Vector< amrex::Vector< amrex::Real > > h_v_geos
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_v_geos
 
amrex::Vector< amrex::Vector< amrex::Vector< amrex::Real > > > h_rayleigh_ptrs
 
amrex::Vector< amrex::Vector< amrex::Vector< amrex::Real > > > h_sponge_ptrs
 
amrex::Vector< amrex::Vector< amrex::Real > > h_sinesq_ptrs
 
amrex::Vector< amrex::Vector< amrex::Real > > h_sinesq_stag_ptrs
 
amrex::Vector< amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > > d_rayleigh_ptrs
 
amrex::Vector< amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > > d_sponge_ptrs
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_sinesq_ptrs
 
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_sinesq_stag_ptrs
 
amrex::Vector< amrex::Realh_havg_density
 
amrex::Vector< amrex::Realh_havg_temperature
 
amrex::Vector< amrex::Realh_havg_pressure
 
amrex::Vector< amrex::Realh_havg_qv
 
amrex::Vector< amrex::Realh_havg_qc
 
amrex::Gpu::DeviceVector< amrex::Reald_havg_density
 
amrex::Gpu::DeviceVector< amrex::Reald_havg_temperature
 
amrex::Gpu::DeviceVector< amrex::Reald_havg_pressure
 
amrex::Gpu::DeviceVector< amrex::Reald_havg_qv
 
amrex::Gpu::DeviceVector< amrex::Reald_havg_qc
 
std::unique_ptr< WriteBndryPlanesm_w2d = nullptr
 
std::unique_ptr< ReadBndryPlanesm_r2d = nullptr
 
std::unique_ptr< SurfaceLayerm_SurfaceLayer = nullptr
 
amrex::Vector< std::unique_ptr< ForestDrag > > m_forest_drag
 
amrex::Vector< amrex::Vector< amrex::BoxArray > > subdomains
 
amrex::Vector< amrex::Realdz_min
 
int line_sampling_interval = -1
 
int plane_sampling_interval = -1
 
amrex::Real line_sampling_per = -one
 
amrex::Real plane_sampling_per = -one
 
std::unique_ptr< LineSamplerline_sampler = nullptr
 
std::unique_ptr< PlaneSamplerplane_sampler = nullptr
 
amrex::Vector< std::unique_ptr< std::fstream > > datalog
 
amrex::Vector< std::unique_ptr< std::fstream > > der_datalog
 
amrex::Vector< std::unique_ptr< std::fstream > > tot_e_datalog
 
amrex::Vector< std::string > datalogname
 
amrex::Vector< std::string > der_datalogname
 
amrex::Vector< std::string > tot_e_datalogname
 
amrex::Vector< std::unique_ptr< std::fstream > > sampleptlog
 
amrex::Vector< std::string > sampleptlogname
 
amrex::Vector< amrex::IntVect > samplepoint
 
amrex::Vector< std::unique_ptr< std::fstream > > samplelinelog
 
amrex::Vector< std::string > samplelinelogname
 
amrex::Vector< amrex::IntVect > sampleline
 
amrex::Vector< std::unique_ptr< eb_ > > eb
 

Static Private Attributes

static int last_plot3d_file_step_1 = -1
 
static int last_plot3d_file_step_2 = -1
 
static int last_plot2d_file_step_1 = -1
 
static int last_plot2d_file_step_2 = -1
 
static int last_check_file_step = -1
 
static amrex::Real last_plot3d_file_time_1 = zero
 
static amrex::Real last_plot3d_file_time_2 = zero
 
static amrex::Real last_plot2d_file_time_1 = zero
 
static amrex::Real last_plot2d_file_time_2 = zero
 
static amrex::Real last_check_file_time = zero
 
static bool plot_file_on_restart = true
 
static amrex::Real start_time = zero
 
static amrex::Real stop_time = std::numeric_limits<amrex::Real>::max()
 
static amrex::Real cfl = Real(0.8)
 
static amrex::Real sub_cfl = one
 
static amrex::Real init_shrink = one
 
static amrex::Real change_max = Real(1.1)
 
static amrex::Real dt_max_initial = bogus_large_value
 
static amrex::Real dt_max = Real(1.0e9)
 
static int fixed_mri_dt_ratio = 0
 
static SolverChoice solverChoice
 
static int verbose = 0
 
static int mg_verbose = 0
 
static bool use_fft = false
 
static int check_for_nans = 0
 
static int sum_interval = -1
 
static int pert_interval = -1
 
static amrex::Real sum_per = -one
 
static PlotFileType plotfile3d_type_1 = PlotFileType::None
 
static PlotFileType plotfile3d_type_2 = PlotFileType::None
 
static PlotFileType plotfile2d_type_1 = PlotFileType::None
 
static PlotFileType plotfile2d_type_2 = PlotFileType::None
 
static StateInterpType interpolation_type
 
static amrex::Vector< amrex::Vector< std::string > > nc_init_file = {{""}}
 
static amrex::Vector< amrex::Vector< int > > have_read_nc_init_file = {{0}}
 
static std::string nc_bdy_file
 
static std::string nc_low_file
 
static int output_1d_column = 0
 
static int column_interval = -1
 
static amrex::Real column_per = -one
 
static amrex::Real column_loc_x = zero
 
static amrex::Real column_loc_y = zero
 
static std::string column_file_name = "column_data.nc"
 
static int output_bndry_planes = 0
 
static int bndry_output_planes_interval = -1
 
static amrex::Real bndry_output_planes_per = -one
 
static amrex::Real bndry_output_planes_start_time = zero
 
static int input_bndry_planes = 0
 
static int ng_dens_hse
 
static int ng_pres_hse
 
static amrex::Vector< amrex::AMRErrorTag > ref_tags
 
static amrex::Real startCPUTime = zero
 
static amrex::Real previousCPUTimeUsed = zero
 

Detailed Description

Main class in ERF code, instantiated from main.cpp

Constructor & Destructor Documentation

◆ ERF() [1/3]

ERF::ERF ( )
22 {
23  int fix_random_seed = 0;
24  ParmParse pp("erf"); pp.query("fix_random_seed", fix_random_seed);
25  // Note that the value of 1024UL is not significant -- the point here is just to set the
26  // same seed for all MPI processes for the purpose of regression testing
27  if (fix_random_seed) {
28  Print() << "Fixing the random seed" << std::endl;
29  InitRandom(1024UL, ParallelDescriptor::NProcs(), 1024UL);
30  }
31 
32  ERF_shared();
33 }
ParmParse pp("prob")
void ERF_shared()
Definition: ERF_Constructors.cpp:61
Here is the call graph for this function:

◆ ~ERF()

ERF::~ERF ( )
overridedefault

◆ ERF() [2/3]

ERF::ERF ( ERF &&  )
deletenoexcept

◆ ERF() [3/3]

ERF::ERF ( const ERF other)
delete

Member Function Documentation

◆ Advance()

void ERF::Advance ( int  lev,
amrex::Real  time,
amrex::Real  dt_lev,
int  iteration,
int  ncycle 
)
private

Function that advances the solution at one level for a single time step – this does some preliminaries then calls erf_advance

Parameters
[in]levlevel of refinement (coarsest level is 0)
[in]timestart time for time advance
[in]dt_levtime step for this time advance
21 {
22  BL_PROFILE("ERF::Advance()");
23 
24  // We must swap the pointers so the previous step's "new" is now this step's "old"
25  std::swap(vars_old[lev], vars_new[lev]);
26 
27  MultiFab& S_old = vars_old[lev][Vars::cons];
28  MultiFab& S_new = vars_new[lev][Vars::cons];
29 
30  MultiFab& U_old = vars_old[lev][Vars::xvel];
31  MultiFab& V_old = vars_old[lev][Vars::yvel];
32  MultiFab& W_old = vars_old[lev][Vars::zvel];
33 
34  MultiFab& U_new = vars_new[lev][Vars::xvel];
35  MultiFab& V_new = vars_new[lev][Vars::yvel];
36  MultiFab& W_new = vars_new[lev][Vars::zvel];
37 
38  // We need to set these because otherwise in the first call to erf_advance we may
39  // read uninitialized data on ghost values in setting the bc's on the velocities
40  U_new.setVal(bogus_large_value,U_new.nGrowVect());
41  V_new.setVal(bogus_large_value,V_new.nGrowVect());
42  W_new.setVal(bogus_large_value,W_new.nGrowVect());
43 
44  //
45  // NOTE: the momenta here are not fillpatched (they are only used as scratch space)
46  // If lev == 0 we have already FillPatched this in ERF::TimeStep
47  //
48  if (lev > 0) {
49  // Set ghost cells to bogus values so they aren't uninitialized
50  W_old.setBndry(bogus_large_value);
51  FillPatchFineLevel(lev, time, {&S_old, &U_old, &V_old, &W_old},
52  {&S_old, &rU_old[lev], &rV_old[lev], &rW_old[lev]},
53  base_state[lev], base_state[lev]);
54  }
55 
56  //
57  // So we must convert the fillpatched to momenta, including the ghost values
58  //
59  const MultiFab* c_vfrac = nullptr;
60  if (solverChoice.terrain_type == TerrainType::EB) {
61  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
62  }
63 
64  VelocityToMomentum(U_old, rU_old[lev].nGrowVect(),
65  V_old, rV_old[lev].nGrowVect(),
66  W_old, rW_old[lev].nGrowVect(),
67  S_old, rU_old[lev], rV_old[lev], rW_old[lev],
68  Geom(lev).Domain(),
69  domain_bcs_type, c_vfrac);
70 
71  // Update the inflow perturbation update time and amplitude
73  {
74  turbPert.calc_tpi_update(lev, dt_lev, U_old, V_old, S_old);
75  }
76 
77  // If PerturbationType::Direct or CPM is selected, directly add the computed perturbation
78  // on the conserved field
80  {
81  auto m_ixtype = S_old.boxArray().ixType(); // Conserved term
82  for (MFIter mfi(S_old,TileNoZ()); mfi.isValid(); ++mfi) {
83  Box bx = mfi.tilebox();
84  const Array4<Real> &cell_data = S_old.array(mfi);
85  const Array4<const Real> &pert_cell = turbPert.pb_cell[lev].array(mfi);
86  turbPert.apply_tpi(lev, bx, RhoTheta_comp, m_ixtype, cell_data, pert_cell);
87  }
88  }
89 
90  // configure SurfaceLayer params if needed
91  if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer) {
92  if (m_SurfaceLayer) {
93  IntVect ng = Theta_prim[lev]->nGrowVect();
94  MultiFab::Copy( *Theta_prim[lev], S_old, RhoTheta_comp, 0, 1, ng);
95  MultiFab::Divide(*Theta_prim[lev], S_old, Rho_comp , 0, 1, ng);
96  if (solverChoice.moisture_type != MoistureType::None) {
97  ng = Qv_prim[lev]->nGrowVect();
98 
99  MultiFab::Copy( *Qv_prim[lev], S_old, RhoQ1_comp, 0, 1, ng);
100  MultiFab::Divide(*Qv_prim[lev], S_old, Rho_comp , 0, 1, ng);
101 
102  if (solverChoice.moisture_indices.qr > -1) {
103  MultiFab::Copy( *Qr_prim[lev], S_old, solverChoice.moisture_indices.qr, 0, 1, ng);
104  MultiFab::Divide(*Qr_prim[lev], S_old, Rho_comp , 0, 1, ng);
105  } else {
106  Qr_prim[lev]->setVal(0);
107  }
108  }
109  // NOTE: std::swap above causes the field ptrs to be out of date.
110  // Reassign the field ptrs for MAC avg computation.
111  m_SurfaceLayer->update_mac_ptrs(lev, vars_old, Theta_prim, Qv_prim, Qr_prim);
112  m_SurfaceLayer->update_pblh(lev, vars_old, z_phys_cc[lev].get(),
114 
115 #ifdef ERF_USE_NETCDF
116  Real elapsed_time_since_start_low = time + (start_time - start_low_time);
117 #else
118  Real elapsed_time_since_start_low = time;
119 #endif
120  m_SurfaceLayer->update_fluxes(lev, time, elapsed_time_since_start_low,
121  S_old, z_phys_nd[lev], walldist[lev]);
122  }
123  }
124 
125 #if defined(ERF_USE_WINDFARM)
126  // **************************************************************************************
127  // Update the windfarm sources
128  // **************************************************************************************
129  if (solverChoice.windfarm_type != WindFarmType::None) {
130  advance_windfarm(Geom(lev), dt_lev, S_old,
131  U_old, V_old, W_old, vars_windfarm[lev],
132  Nturb[lev], SMark[lev], time);
133  }
134 
135 #endif
136 
137  // **************************************************************************************
138  // Update the radiation sources with the "old" state
139  // **************************************************************************************
140  advance_radiation(lev, S_old, dt_lev);
141 
142 #if defined(ERF_USE_EAMXX_SHOC) || defined(ERF_USE_NATIVE_SHOC)
143  // **************************************************************************************
144  // Update the "old" state using SHOC
145  // **************************************************************************************
146  if (solverChoice.turbChoice[lev].uses_shoc_family()) {
147  // Get SFC fluxes from SurfaceLayer
148  if (m_SurfaceLayer) {
149  Vector<const MultiFab*> mfs = {&S_old, &U_old, &V_old, &W_old};
150  m_SurfaceLayer->impose_SurfaceLayer_bcs(lev, mfs, Tau[lev],
151  SFS_hfx1_lev[lev].get() , SFS_hfx2_lev[lev].get() , SFS_hfx3_lev[lev].get(),
152  SFS_q1fx1_lev[lev].get(), SFS_q1fx2_lev[lev].get(), SFS_q1fx3_lev[lev].get(),
153  z_phys_nd[lev].get());
154  }
155 
156  // Apply SHOC before the dycore so it sees a coherent state.
157  Real* w_sub = (solverChoice.custom_w_subsidence) ? d_w_subsid[lev].data() : nullptr;
158  if (solverChoice.turbChoice[lev].uses_eamxx_shoc()) {
159 #ifdef ERF_USE_EAMXX_SHOC
160  compute_shoc_tendencies(lev, &S_old, &U_old, &V_old, &W_old, w_sub,
161  Tau[lev][TauType::tau13].get(), Tau[lev][TauType::tau23].get(),
162  SFS_hfx3_lev[lev].get() , SFS_q1fx3_lev[lev].get() ,
163  eddyDiffs_lev[lev].get() , z_phys_nd[lev].get() ,
164  dt_lev);
165 #endif
166  } else if (solverChoice.turbChoice[lev].uses_native_shoc()) {
167 #ifdef ERF_USE_NATIVE_SHOC
168  compute_native_shoc_tendencies(lev, &S_old, &U_old, &V_old, &W_old, w_sub,
169  Tau[lev][TauType::tau13].get(), Tau[lev][TauType::tau23].get(),
170  SFS_hfx3_lev[lev].get() , SFS_q1fx3_lev[lev].get() ,
171  eddyDiffs_lev[lev].get() , z_phys_nd[lev].get() ,
172  dt_lev);
173 
174  if (native_shoc_driver[lev] && native_shoc_driver[lev]->uses_state_update()) {
175  // Native SHOC updates the old-time state before the dycore reads it.
176  // Re-fill the updated state, velocities, and momenta now so the
177  // pre-dycore checks and strain calculation see coherent fields.
178  Vector<MultiFab*> mfs_vel = {&S_old, &U_old, &V_old, &W_old};
179  if (lev == 0) {
180  FillPatchCrseLevel(lev, time, mfs_vel, false);
181  VelocityToMomentum(U_old, rU_old[lev].nGrowVect(),
182  V_old, rV_old[lev].nGrowVect(),
183  W_old, rW_old[lev].nGrowVect(),
184  S_old, rU_old[lev], rV_old[lev], rW_old[lev],
185  Geom(lev).Domain(),
186  domain_bcs_type, c_vfrac);
187  } else {
188  Vector<MultiFab*> mfs_mom = {&S_old, &rU_old[lev], &rV_old[lev], &rW_old[lev]};
189  FillPatchFineLevel(lev, time, mfs_vel, mfs_mom,
190  base_state[lev], base_state[lev],
191  true, false);
192  }
193  }
194 #endif
195  }
196  }
197 #endif
198 
199  const BoxArray& ba = S_old.boxArray();
200  const DistributionMapping& dm = S_old.DistributionMap();
201 
202  int nvars = S_old.nComp();
203 
204  // Source array for conserved cell-centered quantities -- this will be filled
205  // in the call to make_sources in ERF_TI_slow_rhs_pre.H
206  MultiFab cc_source(ba,dm,nvars,1); cc_source.setVal(0);
207 
208  // Source arrays for momenta -- these will be filled
209  // in the call to make_mom_sources in ERF_TI_slow_rhs_pre.H
210  BoxArray ba_x(ba); ba_x.surroundingNodes(0);
211  MultiFab xmom_source(ba_x,dm,1,1); xmom_source.setVal(0);
212 
213  BoxArray ba_y(ba); ba_y.surroundingNodes(1);
214  MultiFab ymom_source(ba_y,dm,1,1); ymom_source.setVal(0);
215 
216  BoxArray ba_z(ba); ba_z.surroundingNodes(2);
217  MultiFab zmom_source(ba_z,dm,1,1); zmom_source.setVal(0);
218  MultiFab buoyancy(ba_z,dm,1,1); buoyancy.setVal(0);
219 
220  amrex::Vector<MultiFab> state_old;
221  amrex::Vector<MultiFab> state_new;
222 
223  // **************************************************************************************
224  // Here we define state_old and state_new which are to be advanced
225  // **************************************************************************************
226  // Initial solution
227  // Note that "old" and "new" here are relative to each RK stage.
228  state_old.push_back(MultiFab(S_old , amrex::make_alias, 0, nvars)); // cons
229  state_old.push_back(MultiFab(rU_old[lev], amrex::make_alias, 0, 1)); // xmom
230  state_old.push_back(MultiFab(rV_old[lev], amrex::make_alias, 0, 1)); // ymom
231  state_old.push_back(MultiFab(rW_old[lev], amrex::make_alias, 0, 1)); // zmom
232 
233  // Final solution
234  // state_new at the end of the last RK stage holds the t^{n+1} data
235  state_new.push_back(MultiFab(S_new , amrex::make_alias, 0, nvars)); // cons
236  state_new.push_back(MultiFab(rU_new[lev], amrex::make_alias, 0, 1)); // xmom
237  state_new.push_back(MultiFab(rV_new[lev], amrex::make_alias, 0, 1)); // ymom
238  state_new.push_back(MultiFab(rW_new[lev], amrex::make_alias, 0, 1)); // zmom
239 
240  // **************************************************************************************
241  // Tests on the reasonableness of the solution before the dycore
242  // **************************************************************************************
243  // Test for NaNs after dycore
244  if (check_for_nans > 1) {
245  if (verbose > 1) {
246  amrex::Print() << "Testing old state and vels for NaNs before dycore" << std::endl;
247  }
248  check_state_for_nans(S_old);
249  check_vels_for_nans(rU_old[lev],rV_old[lev],rW_old[lev]);
250  }
251 
252  // We only test on low temp if we have a moisture model because we are protecting against
253  // the test on low temp inside the moisture models
254  if (solverChoice.moisture_type != MoistureType::None) {
255  if (verbose > 1) {
256  amrex::Print() << "Testing on low temperature before dycore" << std::endl;
257  }
258  check_for_low_temp(S_old);
259  } else {
260  if (verbose > 1) {
261  amrex::Print() << "Testing on negative temperature before dycore" << std::endl;
262  }
264  }
265 
266  // **************************************************************************************
267  // Update the dycore
268  // **************************************************************************************
269  advance_dycore(lev, state_old, state_new,
270  U_old, V_old, W_old,
271  U_new, V_new, W_new,
272  cc_source, xmom_source, ymom_source, zmom_source, buoyancy,
273  Geom(lev), dt_lev, time);
274 
275  // **************************************************************************************
276  // Tests on the reasonableness of the solution after the dycore
277  // **************************************************************************************
278  // Test for NaNs after dycore
279  if (check_for_nans > 0) {
280  if (verbose > 1) {
281  amrex::Print() << "Testing new state and vels for NaNs after dycore" << std::endl;
282  }
283  check_state_for_nans(S_new);
284  check_vels_for_nans(rU_new[lev],rV_new[lev],rW_new[lev]);
285  }
286 
287  // We only test on low temp if we have a moisture model because we are protecting against
288  // the test on low temp inside the moisture models
289  if (solverChoice.moisture_type != MoistureType::None) {
290  if (verbose > 1) {
291  amrex::Print() << "Testing on low temperature after dycore" << std::endl;
292  }
293  check_for_low_temp(S_new);
294  } else {
295  // Otherwise we will test on negative (rhotheta) coming out of the dycore
296  if (verbose > 1) {
297  amrex::Print() << "Testing on negative temperature after dycore" << std::endl;
298  }
300  }
301 
302  // **************************************************************************************
303  // Update the microphysics (moisture)
304  // **************************************************************************************
306  {
307  advance_microphysics(lev, S_new, dt_lev, iteration, time);
308 
309  // Test for NaNs after microphysics
310  if (check_for_nans > 0) {
311  amrex::Print() << "Testing new state for NaNs after advance_microphysics" << std::endl;
312  check_state_for_nans(S_new);
313  }
314  }
315 
316  // **************************************************************************************
317  // Update the land surface model
318  // **************************************************************************************
319  Real time_at_end_of_step = time+dt_lev;
320  advance_lsm(lev, S_new, U_new, V_new, time_at_end_of_step, dt_lev);
321 
322 #ifdef ERF_USE_PARTICLES
323  // **************************************************************************************
324  // Update the particle positions
325  // **************************************************************************************
326  evolveTracers(lev, dt_lev, vars_new, z_phys_nd);
327 #endif
328 
329  // ***********************************************************************************************
330  // Impose domain boundary conditions here so that in FillPatching the fine data we won't
331  // need to re-fill these
332  // ***********************************************************************************************
333  if (lev < finest_level) {
334  IntVect ngvect_vels = vars_new[lev][Vars::xvel].nGrowVect();
336  0,vars_new[lev][Vars::cons].nComp(),
337  vars_new[lev][Vars::cons].nGrowVect(),time,BCVars::cons_bc,true);
338  (*physbcs_u[lev])(vars_new[lev][Vars::xvel], vars_new[lev][Vars::xvel], vars_new[lev][Vars::yvel],
339  ngvect_vels,time,BCVars::xvel_bc,true);
340  (*physbcs_v[lev])(vars_new[lev][Vars::yvel], vars_new[lev][Vars::xvel], vars_new[lev][Vars::yvel],
341  ngvect_vels,time,BCVars::yvel_bc,true);
342  (*physbcs_w[lev])(vars_new[lev][Vars::zvel], vars_new[lev][Vars::xvel], vars_new[lev][Vars::yvel],
343  ngvect_vels,time,BCVars::zvel_bc,true);
344  }
345 
346  // **************************************************************************************
347  // Register old and new coarse data if we are at a level less than the finest level
348  // **************************************************************************************
349  if (lev < finest_level) {
350  if (cf_width > 0) {
351  // We must fill the ghost cells of these so that the parallel copy works correctly
352  state_old[IntVars::cons].FillBoundary(geom[lev].periodicity());
353  state_new[IntVars::cons].FillBoundary(geom[lev].periodicity());
354  FPr_c[lev].RegisterCoarseData({&state_old[IntVars::cons], &state_new[IntVars::cons]},
355  {time, time+dt_lev});
356  }
357 
358  if (cf_width >= 0) {
359  // We must fill the ghost cells of these so that the parallel copy works correctly
360  state_old[IntVars::xmom].FillBoundary(geom[lev].periodicity());
361  state_new[IntVars::xmom].FillBoundary(geom[lev].periodicity());
362  FPr_u[lev].RegisterCoarseData({&state_old[IntVars::xmom], &state_new[IntVars::xmom]},
363  {time, time+dt_lev});
364 
365  state_old[IntVars::ymom].FillBoundary(geom[lev].periodicity());
366  state_new[IntVars::ymom].FillBoundary(geom[lev].periodicity());
367  FPr_v[lev].RegisterCoarseData({&state_old[IntVars::ymom], &state_new[IntVars::ymom]},
368  {time, time+dt_lev});
369 
370  state_old[IntVars::zmom].FillBoundary(geom[lev].periodicity());
371  state_new[IntVars::zmom].FillBoundary(geom[lev].periodicity());
372  FPr_w[lev].RegisterCoarseData({&state_old[IntVars::zmom], &state_new[IntVars::zmom]},
373  {time, time+dt_lev});
374  }
375 
376  //
377  // Now create a MultiFab that holds (S_new - S_old) / dt from the coarse level interpolated
378  // on to the coarse/fine boundary at the fine resolution
379  //
380  Interpolater* mapper_f = &face_cons_linear_interp;
381 
382  // PhysBCFunctNoOp null_bc;
383  // MultiFab tempx(vars_new[lev+1][Vars::xvel].boxArray(),vars_new[lev+1][Vars::xvel].DistributionMap(),1,0);
384  // tempx.setVal(0);
385  // xmom_crse_rhs[lev+1].setVal(0);
386  // FPr_u[lev].FillSet(tempx , time , null_bc, domain_bcs_type);
387  // FPr_u[lev].FillSet(xmom_crse_rhs[lev+1], time+dt_lev, null_bc, domain_bcs_type);
388  // MultiFab::Subtract(xmom_crse_rhs[lev+1],tempx,0,0,1,IntVect{0});
389  // xmom_crse_rhs[lev+1].mult(one/dt_lev,0,1,0);
390 
391  // MultiFab tempy(vars_new[lev+1][Vars::yvel].boxArray(),vars_new[lev+1][Vars::yvel].DistributionMap(),1,0);
392  // tempy.setVal(0);
393  // ymom_crse_rhs[lev+1].setVal(0);
394  // FPr_v[lev].FillSet(tempy , time , null_bc, domain_bcs_type);
395  // FPr_v[lev].FillSet(ymom_crse_rhs[lev+1], time+dt_lev, null_bc, domain_bcs_type);
396  // MultiFab::Subtract(ymom_crse_rhs[lev+1],tempy,0,0,1,IntVect{0});
397  // ymom_crse_rhs[lev+1].mult(one/dt_lev,0,1,0);
398 
399  MultiFab temp_state(zmom_crse_rhs[lev+1].boxArray(),zmom_crse_rhs[lev+1].DistributionMap(),1,0);
400  InterpFromCoarseLevel(temp_state, IntVect{0}, IntVect{0}, state_old[IntVars::zmom], 0, 0, 1,
401  geom[lev], geom[lev+1], refRatio(lev), mapper_f, domain_bcs_type, BCVars::zvel_bc);
402  InterpFromCoarseLevel(zmom_crse_rhs[lev+1], IntVect{0}, IntVect{0}, state_new[IntVars::zmom], 0, 0, 1,
403  geom[lev], geom[lev+1], refRatio(lev), mapper_f, domain_bcs_type, BCVars::zvel_bc);
404  MultiFab::Subtract(zmom_crse_rhs[lev+1],temp_state,0,0,1,IntVect{0});
405  zmom_crse_rhs[lev+1].mult(one/dt_lev,0,1,0);
406  }
407 
408  // ***********************************************************************************************
409  // Update the time averaged velocities if they are requested
410  // ***********************************************************************************************
412  Time_Avg_Vel_atCC(dt[lev], t_avg_cnt[lev], vel_t_avg[lev].get(), U_new, V_new, W_new);
413  }
414 }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:26
constexpr amrex::Real one
Definition: ERF_Constants.H:9
@ tau23
Definition: ERF_DataStruct.H:32
@ tau13
Definition: ERF_DataStruct.H:32
@ nvars
Definition: ERF_DataStruct.H:98
#define Rho_comp
Definition: ERF_IndexDefines.H:36
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:42
@ surface_layer
pp get("wavelength", wavelength)
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
void Time_Avg_Vel_atCC(const Real &dt, Real &t_avg_cnt, MultiFab *vel_t_avg, MultiFab &xvel, MultiFab &yvel, MultiFab &zvel)
Definition: ERF_TimeAvgVel.cpp:9
void VelocityToMomentum(const amrex::MultiFab &xvel_in, const amrex::IntVect &xvel_ngrow, const amrex::MultiFab &yvel_in, const amrex::IntVect &yvel_ngrow, const amrex::MultiFab &zvel_in, const amrex::IntVect &zvel_ngrow, const amrex::MultiFab &cons_in, amrex::MultiFab &xmom_out, amrex::MultiFab &ymom_out, amrex::MultiFab &zmom_out, const amrex::Box &domain, const amrex::Vector< amrex::BCRec > &domain_bcs_type_h, const amrex::MultiFab *c_vfrac=nullptr)
amrex::Vector< amrex::MultiFab > rU_new
Definition: ERF.H:939
amrex::Vector< std::unique_ptr< amrex::MultiFab > > walldist
Definition: ERF.H:1053
static amrex::Real start_time
Definition: ERF.H:1139
void check_vels_for_nans(amrex::MultiFab const &xvel, amrex::MultiFab const &yvel, amrex::MultiFab const &zvel)
Definition: ERF.cpp:2857
amrex::Vector< ERFFillPatcher > FPr_u
Definition: ERF.H:993
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx3_lev
Definition: ERF.H:1016
amrex::Vector< amrex::Vector< amrex::MultiFab > > vars_new
Definition: ERF.H:904
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx3_lev
Definition: ERF.H:1014
amrex::Vector< ERFFillPatcher > FPr_v
Definition: ERF.H:994
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx1_lev
Definition: ERF.H:1014
eb_ const & get_eb(int lev) const noexcept
Definition: ERF.H:1733
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_cons > > physbcs_cons
Definition: ERF.H:926
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_cc
Definition: ERF.H:1030
amrex::Vector< std::unique_ptr< amrex::MultiFab > > eddyDiffs_lev
Definition: ERF.H:1000
static SolverChoice solverChoice
Definition: ERF.H:1270
amrex::Vector< ERFFillPatcher > FPr_c
Definition: ERF.H:992
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > Tau
Definition: ERF.H:998
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vel_t_avg
Definition: ERF.H:911
static int verbose
Definition: ERF.H:1305
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_w > > physbcs_w
Definition: ERF.H:929
amrex::Vector< amrex::MultiFab > base_state
Definition: ERF.H:1064
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Qv_prim
Definition: ERF.H:934
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx2_lev
Definition: ERF.H:1016
amrex::Vector< amrex::MultiFab > rV_new
Definition: ERF.H:941
amrex::Vector< amrex::BCRec > domain_bcs_type
Definition: ERF.H:1080
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Qr_prim
Definition: ERF.H:935
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_u > > physbcs_u
Definition: ERF.H:927
amrex::Vector< amrex::Real > t_avg_cnt
Definition: ERF.H:912
amrex::Vector< amrex::MultiFab > rU_old
Definition: ERF.H:938
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Theta_prim
Definition: ERF.H:933
static int check_for_nans
Definition: ERF.H:1309
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_v > > physbcs_v
Definition: ERF.H:928
void check_state_for_nans(amrex::MultiFab const &S)
Definition: ERF.cpp:2790
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd
Definition: ERF.H:1029
void advance_dycore(int level, amrex::Vector< amrex::MultiFab > &state_old, amrex::Vector< amrex::MultiFab > &state_new, amrex::MultiFab &xvel_old, amrex::MultiFab &yvel_old, amrex::MultiFab &zvel_old, amrex::MultiFab &xvel_new, amrex::MultiFab &yvel_new, amrex::MultiFab &zvel_new, amrex::MultiFab &source, amrex::MultiFab &xmom_src, amrex::MultiFab &ymom_src, amrex::MultiFab &zmom_src, amrex::MultiFab &buoyancy, amrex::Geometry fine_geom, amrex::Real dt, amrex::Real time)
Definition: ERF_AdvanceDycore.cpp:38
amrex::Vector< amrex::MultiFab > rW_new
Definition: ERF.H:943
void FillPatchCrseLevel(int lev, double time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, bool cons_only=false)
Definition: ERF_FillPatch.cpp:290
amrex::Vector< amrex::MultiFab > zmom_crse_rhs
Definition: ERF.H:947
void check_for_low_temp(amrex::MultiFab &S)
Definition: ERF.cpp:2884
TurbulentPerturbation turbPert
Definition: ERF.H:1273
amrex::Vector< amrex::MultiFab > rW_old
Definition: ERF.H:942
void check_for_negative_theta(amrex::MultiFab &S)
Definition: ERF.cpp:2919
std::unique_ptr< SurfaceLayer > m_SurfaceLayer
Definition: ERF.H:1446
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_w_subsid
Definition: ERF.H:1397
amrex::Vector< ERFFillPatcher > FPr_w
Definition: ERF.H:995
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx2_lev
Definition: ERF.H:1014
amrex::Vector< amrex::Real > dt
Definition: ERF.H:898
void advance_radiation(int lev, amrex::MultiFab &cons_in, const amrex::Real &dt_advance)
Definition: ERF_AdvanceRadiation.cpp:5
void advance_microphysics(int lev, amrex::MultiFab &cons_in, const amrex::Real &dt_advance, const int &iteration, const amrex::Real &time)
Definition: ERF_AdvanceMicrophysics.cpp:5
int cf_width
Definition: ERF.H:990
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx1_lev
Definition: ERF.H:1016
amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > phys_bc_type
Definition: ERF.H:1096
amrex::Vector< amrex::MultiFab > rV_old
Definition: ERF.H:940
void FillPatchFineLevel(int lev, double time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, const amrex::Vector< amrex::MultiFab * > &mfs_mom, const amrex::MultiFab &old_base_state, const amrex::MultiFab &new_base_state, bool fillset=true, bool cons_only=false)
Definition: ERF_FillPatch.cpp:20
void advance_lsm(int lev, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, const amrex::Real &time, const amrex::Real &dt_advance)
Definition: ERF_AdvanceLSM.cpp:5
amrex::Vector< amrex::Vector< amrex::MultiFab > > vars_old
Definition: ERF.H:905
const std::unique_ptr< amrex::EBFArrayBoxFactory > & get_const_factory() const noexcept
Definition: ERF_EB.H:46
@ zvel_bc
Definition: ERF_IndexDefines.H:104
@ yvel_bc
Definition: ERF_IndexDefines.H:103
@ cons_bc
Definition: ERF_IndexDefines.H:86
@ xvel_bc
Definition: ERF_IndexDefines.H:102
@ ymom
Definition: ERF_IndexDefines.H:194
@ cons
Definition: ERF_IndexDefines.H:192
@ zmom
Definition: ERF_IndexDefines.H:195
@ xmom
Definition: ERF_IndexDefines.H:193
@ ng
Definition: ERF_Morrison.H:48
@ xvel
Definition: ERF_IndexDefines.H:175
@ cons
Definition: ERF_IndexDefines.H:174
@ zvel
Definition: ERF_IndexDefines.H:177
@ yvel
Definition: ERF_IndexDefines.H:176
int qr
Definition: ERF_DataStruct.H:110
bool moisture_tight_coupling
Definition: ERF_DataStruct.H:1408
bool custom_w_subsidence
Definition: ERF_DataStruct.H:1316
bool use_perturbation(int lev) const
Definition: ERF_DataStruct.H:1341
bool use_direct_perturbation(int lev) const
Definition: ERF_DataStruct.H:1348
amrex::Vector< TurbChoice > turbChoice
Definition: ERF_DataStruct.H:1223
MoistureType moisture_type
Definition: ERF_DataStruct.H:1389
static TerrainType terrain_type
Definition: ERF_DataStruct.H:1202
WindFarmType windfarm_type
Definition: ERF_DataStruct.H:1390
MoistureComponentIndices moisture_indices
Definition: ERF_DataStruct.H:1406
bool time_avg_vel
Definition: ERF_DataStruct.H:1339
amrex::Vector< amrex::MultiFab > pb_cell
Definition: ERF_TurbPertStruct.H:667
void calc_tpi_update(const int lev, const amrex::Real dt, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel, amrex::MultiFab &mf_cons)
Definition: ERF_TurbPertStruct.H:250
void apply_tpi(const int &lev, const amrex::Box &vbx, const int &comp, const amrex::IndexType &m_ixtype, const amrex::Array4< amrex::Real > &src_arr, const amrex::Array4< amrex::Real const > &pert_cell)
Definition: ERF_TurbPertStruct.H:351
Here is the call graph for this function:

◆ advance_dycore()

void ERF::advance_dycore ( int  level,
amrex::Vector< amrex::MultiFab > &  state_old,
amrex::Vector< amrex::MultiFab > &  state_new,
amrex::MultiFab &  xvel_old,
amrex::MultiFab &  yvel_old,
amrex::MultiFab &  zvel_old,
amrex::MultiFab &  xvel_new,
amrex::MultiFab &  yvel_new,
amrex::MultiFab &  zvel_new,
amrex::MultiFab &  source,
amrex::MultiFab &  xmom_src,
amrex::MultiFab &  ymom_src,
amrex::MultiFab &  zmom_src,
amrex::MultiFab &  buoyancy,
amrex::Geometry  fine_geom,
amrex::Real  dt,
amrex::Real  time 
)

Function that advances the solution at one level for a single time step – this sets up the multirate time integrator and calls the integrator's advance function

Parameters
[in]levellevel of refinement (coarsest level is 0)
[in]state_oldold-time conserved variables
[in]state_newnew-time conserved variables
[in]xvel_oldold-time x-component of velocity
[in]yvel_oldold-time y-component of velocity
[in]zvel_oldold-time z-component of velocity
[in]xvel_newnew-time x-component of velocity
[in]yvel_newnew-time y-component of velocity
[in]zvel_newnew-time z-component of velocity
[in]cc_srcsource term for conserved variables
[in]xmom_srcsource term for x-momenta
[in]ymom_srcsource term for y-momenta
[in]zmom_srcsource term for z-momenta
[in]fine_geomcontainer for geometry information at current level
[in]dt_advancetime step for this time advance
[in]old_timeold time for this time advance
48 {
49  BL_PROFILE_VAR("erf_advance_dycore()",erf_advance_dycore);
50 
51  const Box& domain = fine_geom.Domain();
52 
55 
56  MultiFab r_hse (base_state[level], make_alias, BaseState::r0_comp , 1);
57  MultiFab p_hse (base_state[level], make_alias, BaseState::p0_comp , 1);
58  MultiFab pi_hse(base_state[level], make_alias, BaseState::pi0_comp, 1);
59 
60  // These pointers are used in the MRI utility functions
61  MultiFab* r0 = &r_hse;
62  MultiFab* p0 = &p_hse;
63  MultiFab* pi0 = &pi_hse;
64 
65  MultiFab* rhotheta_src_ptr = solverChoice.custom_rhotheta_forcing ? rhotheta_src[level].get() : nullptr;
66  MultiFab* rhoqt_src_ptr = solverChoice.custom_moisture_forcing ? rhoqt_src[level].get() : nullptr;
67  Real* dptr_wbar_sub = solverChoice.custom_w_subsidence ? d_w_subsid[level].data() : nullptr;
68 
69  // Turbulent Perturbation Pointer
70  //Real* dptr_rhotheta_src = solverChoice.pert_type ? d_rhotheta_src[level].data() : nullptr;
71 
72  Vector<Real*> d_rayleigh_ptrs_at_lev;
73  d_rayleigh_ptrs_at_lev.resize(Rayleigh::nvars);
74  d_rayleigh_ptrs_at_lev[Rayleigh::ubar] = solverChoice.dampingChoice.rayleigh_damp_U ? d_rayleigh_ptrs[level][Rayleigh::ubar].data() : nullptr;
75  d_rayleigh_ptrs_at_lev[Rayleigh::vbar] = solverChoice.dampingChoice.rayleigh_damp_V ? d_rayleigh_ptrs[level][Rayleigh::vbar].data() : nullptr;
76  d_rayleigh_ptrs_at_lev[Rayleigh::wbar] = solverChoice.dampingChoice.rayleigh_damp_W ? d_rayleigh_ptrs[level][Rayleigh::wbar].data() : nullptr;
77  d_rayleigh_ptrs_at_lev[Rayleigh::thetabar] = solverChoice.dampingChoice.rayleigh_damp_T ? d_rayleigh_ptrs[level][Rayleigh::thetabar].data() : nullptr;
78 
79  bool use_rayleigh =
82  Real* d_sinesq_at_lev = (use_rayleigh) ? d_sinesq_ptrs[level].data() : nullptr;
83  Real* d_sinesq_stag_at_lev = (use_rayleigh) ? d_sinesq_stag_ptrs[level].data() : nullptr;
84 
85  Vector<Real*> d_sponge_ptrs_at_lev;
86  if (SpongeChoice::sponge_type == SpongeType::Input_Sponge)
87  {
88  d_sponge_ptrs_at_lev.resize(Sponge::nvars_sponge);
89  d_sponge_ptrs_at_lev[Sponge::ubar_sponge] = d_sponge_ptrs[level][Sponge::ubar_sponge].data();
90  d_sponge_ptrs_at_lev[Sponge::vbar_sponge] = d_sponge_ptrs[level][Sponge::vbar_sponge].data();
91  }
92 
93  bool l_use_terrain_fitted_coords = (solverChoice.mesh_type != MeshType::ConstantDz);
94  bool l_use_kturb = tc.use_kturb;
95  bool l_use_diff = ( (dc.molec_diff_type != MolecDiffType::None) ||
96  l_use_kturb );
97 
98  const bool use_SurfLayer = (m_SurfaceLayer != nullptr);
99  const MultiFab* z_0 = (use_SurfLayer) ? m_SurfaceLayer->get_z0(level) : nullptr;
100 
101  const BoxArray& ba = state_old[IntVars::cons].boxArray();
102  const BoxArray& ba_z = zvel_old.boxArray();
103  const DistributionMapping& dm = state_old[IntVars::cons].DistributionMap();
104 
105  int num_prim = state_old[IntVars::cons].nComp() - 1;
106 
107  MultiFab S_prim (ba , dm, num_prim, state_old[IntVars::cons].nGrowVect());
108  MultiFab pi_stage (ba , dm, 1, 1);
109  MultiFab fast_coeffs(ba_z, dm, 5, 0);
110 
111  MultiFab* eddyDiffs = eddyDiffs_lev[level].get();
112  MultiFab* SmnSmn = SmnSmn_lev[level].get();
113 
114  // **************************************************************************************
115  // Compute strain for use in slow RHS and Smagorinsky model
116  // **************************************************************************************
117  {
118  BL_PROFILE("erf_advance_strain");
119  if (l_use_diff) {
120 
121  const BCRec* bc_ptr_h = domain_bcs_type.data();
122  const GpuArray<Real, AMREX_SPACEDIM> dxInv = fine_geom.InvCellSizeArray();
123 
124 #ifdef _OPENMP
125 #pragma omp parallel if (Gpu::notInLaunchRegion())
126 #endif
127  for ( MFIter mfi(state_new[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
128  {
129  Box bxcc = mfi.growntilebox(IntVect(1,1,0));
130  Box tbxxy = mfi.tilebox(IntVect(1,1,0),IntVect(1,1,0));
131  Box tbxxz = mfi.tilebox(IntVect(1,0,1),IntVect(1,1,0));
132  Box tbxyz = mfi.tilebox(IntVect(0,1,1),IntVect(1,1,0));
133 
134  if (bxcc.smallEnd(2) != domain.smallEnd(2)) {
135  bxcc.growLo(2,1);
136  tbxxy.growLo(2,1);
137  tbxxz.growLo(2,1);
138  tbxyz.growLo(2,1);
139  }
140 
141  if (bxcc.bigEnd(2) != domain.bigEnd(2)) {
142  bxcc.growHi(2,1);
143  tbxxy.growHi(2,1);
144  tbxxz.growHi(2,1);
145  tbxyz.growHi(2,1);
146  }
147 
148  const Array4<const Real> & u = xvel_old.array(mfi);
149  const Array4<const Real> & v = yvel_old.array(mfi);
150  const Array4<const Real> & w = zvel_old.array(mfi);
151 
152  Array4<Real> tau11 = Tau[level][TauType::tau11].get()->array(mfi);
153  Array4<Real> tau22 = Tau[level][TauType::tau22].get()->array(mfi);
154  Array4<Real> tau33 = Tau[level][TauType::tau33].get()->array(mfi);
155  Array4<Real> tau12 = Tau[level][TauType::tau12].get()->array(mfi);
156  Array4<Real> tau13 = Tau[level][TauType::tau13].get()->array(mfi);
157  Array4<Real> tau23 = Tau[level][TauType::tau23].get()->array(mfi);
158 
159  Array4<Real> tau21 = l_use_terrain_fitted_coords ? Tau[level][TauType::tau21].get()->array(mfi) : Array4<Real>{};
160  Array4<Real> tau31 = l_use_terrain_fitted_coords ? Tau[level][TauType::tau31].get()->array(mfi) : Array4<Real>{};
161  Array4<Real> tau32 = l_use_terrain_fitted_coords ? Tau[level][TauType::tau32].get()->array(mfi) : Array4<Real>{};
162  const Array4<const Real>& z_nd = z_phys_nd[level]->const_array(mfi);
163 
164  const Array4<const Real> mf_mx = mapfac[level][MapFacType::m_x]->const_array(mfi);
165  const Array4<const Real> mf_ux = mapfac[level][MapFacType::u_x]->const_array(mfi);
166  const Array4<const Real> mf_vx = mapfac[level][MapFacType::v_x]->const_array(mfi);
167  const Array4<const Real> mf_my = mapfac[level][MapFacType::m_y]->const_array(mfi);
168  const Array4<const Real> mf_uy = mapfac[level][MapFacType::u_y]->const_array(mfi);
169  const Array4<const Real> mf_vy = mapfac[level][MapFacType::v_y]->const_array(mfi);
170 
171  // We update Tau_corr[level] in erf_make_tau_terms, not here
172  Array4<Real> no_tau_corr_update_here{};
173 
174  if (solverChoice.mesh_type == MeshType::StretchedDz) {
175  ComputeStrain_S(bxcc, tbxxy, tbxxz, tbxyz, domain,
176  u, v, w,
177  tau11, tau22, tau33,
178  tau12, tau21,
179  tau13, tau31,
180  tau23, tau32,
181  stretched_dz_d[level], dxInv,
182  mf_mx, mf_ux, mf_vx, mf_my, mf_uy, mf_vy, bc_ptr_h,
183  no_tau_corr_update_here, no_tau_corr_update_here);
184  } else if (l_use_terrain_fitted_coords) {
185  ComputeStrain_T(bxcc, tbxxy, tbxxz, tbxyz, domain,
186  u, v, w,
187  tau11, tau22, tau33,
188  tau12, tau21,
189  tau13, tau31,
190  tau23, tau32,
191  z_nd, detJ_cc[level]->const_array(mfi), dxInv,
192  mf_mx, mf_ux, mf_vx, mf_my, mf_uy, mf_vy, bc_ptr_h,
193  no_tau_corr_update_here, no_tau_corr_update_here);
194  } else {
195  if (solverChoice.terrain_type == TerrainType::EB) {
196  ComputeStrain_EB(mfi, bxcc, tbxxy, tbxxz, tbxyz, domain,
197  u, v, w,
198  tau11, tau22, tau33,
199  tau12, tau13, tau23,
200  dxInv,
201  bc_ptr_h,
202  get_eb(level),
203  no_tau_corr_update_here, no_tau_corr_update_here);
204  } else {
205  ComputeStrain_N(bxcc, tbxxy, tbxxz, tbxyz, domain,
206  u, v, w,
207  tau11, tau22, tau33,
208  tau12, tau13, tau23,
209  dxInv,
210  mf_mx, mf_ux, mf_vx, mf_my, mf_uy, mf_vy, bc_ptr_h,
211  no_tau_corr_update_here, no_tau_corr_update_here);
212  }
213  }
214  } // mfi
215  } // l_use_diff
216  } // profile
217 
218 #include "ERF_TI_utils.H"
219 
220  // Additional SFS quantities, calculated once per timestep
221  MultiFab* Hfx1 = SFS_hfx1_lev[level].get();
222  MultiFab* Hfx2 = SFS_hfx2_lev[level].get();
223  MultiFab* Hfx3 = SFS_hfx3_lev[level].get();
224  MultiFab* Q1fx1 = SFS_q1fx1_lev[level].get();
225  MultiFab* Q1fx2 = SFS_q1fx2_lev[level].get();
226  MultiFab* Q1fx3 = SFS_q1fx3_lev[level].get();
227  MultiFab* Q2fx3 = SFS_q2fx3_lev[level].get();
228  MultiFab* Diss = SFS_diss_lev[level].get();
229 
230  MultiFab* Hfx3_EB = nullptr;
231  if (solverChoice.terrain_type == TerrainType::EB) {
232  Hfx3_EB = hfx3_EB[level].get();
233  }
234 
235  // *************************************************************************
236  // Calculate cell-centered eddy viscosity & diffusivities
237  //
238  // Notes -- we fill all the data in ghost cells before calling this so
239  // that we can fill the eddy viscosity in the ghost regions and
240  // not have to call a boundary filler on this data itself
241  //
242  // LES - updates both horizontal and vertical eddy viscosity components
243  // PBL - only updates vertical eddy viscosity components so horizontal
244  // components come from the LES model or are left as zero.
245  // *************************************************************************
246  if (l_use_kturb)
247  {
248  // NOTE: state_new transfers to state_old for PBL (due to ptr swap in advance)
249  bool l_use_moisture = ( solverChoice.moisture_type != MoistureType::None );
250  const BCRec* bc_ptr_h = domain_bcs_type.data();
251  ComputeTurbulentViscosity(dt_advance, xvel_old, yvel_old,Tau[level],
252  state_old[IntVars::cons],
253  *walldist[level].get(),
254  *eddyDiffs, *Hfx1, *Hfx2, *Hfx3, *Diss, // to be updated
255  fine_geom, mapfac[level],
256  z_phys_nd[level], solverChoice,
257  m_SurfaceLayer, z_0, l_use_terrain_fitted_coords,
258  l_use_moisture, level,
259  bc_ptr_h,
260  get_eb(level));
261  }
262 
263  // ***********************************************************************************************
264  // Update user-defined source terms -- these are defined once per time step (not per RK stage)
265  // ***********************************************************************************************
267  prob->update_rhotheta_sources(old_time,
268  rhotheta_src_ptr,
269  fine_geom, z_phys_cc[level]);
270  }
271 
273  prob->update_rhoqt_sources(old_time,
274  rhoqt_src_ptr,
275  fine_geom, z_phys_cc[level]);
276  }
277 
279  prob->update_geostrophic_profile(old_time,
280  h_u_geos[level], d_u_geos[level],
281  h_v_geos[level], d_v_geos[level],
282  fine_geom, z_phys_cc[level]);
283  }
284 
286  prob->update_w_subsidence(old_time,
287  h_w_subsid[level], d_w_subsid[level],base_state[level],
288  fine_geom, z_phys_nd[level]);
289  }
290 
291  // ***********************************************************************************************
292  // Convert old velocity available on faces to old momentum on faces to be used in time integration
293  // ***********************************************************************************************
294  MultiFab density(state_old[IntVars::cons], make_alias, Rho_comp, 1);
295 
296  //
297  // This is an optimization since we won't need more than one ghost
298  // cell of momentum in the integrator if not using numerical diffusion
299  //
300  IntVect ngu = (!solverChoice.use_num_diff) ? IntVect(1,1,1) : xvel_old.nGrowVect();
301  IntVect ngv = (!solverChoice.use_num_diff) ? IntVect(1,1,1) : yvel_old.nGrowVect();
302  IntVect ngw = (!solverChoice.use_num_diff) ? IntVect(1,1,0) : zvel_old.nGrowVect();
303 
304  const MultiFab* c_vfrac = nullptr;
305  if (solverChoice.terrain_type == TerrainType::EB) {
306  c_vfrac = &((get_eb(level).get_const_factory())->getVolFrac());
307  }
308 
309  VelocityToMomentum(xvel_old, ngu, yvel_old, ngv, zvel_old, ngw, density,
310  state_old[IntVars::xmom],
311  state_old[IntVars::ymom],
312  state_old[IntVars::zmom],
313  domain, domain_bcs_type, c_vfrac);
314 
315  MultiFab::Copy(xvel_new,xvel_old,0,0,1,xvel_old.nGrowVect());
316  MultiFab::Copy(yvel_new,yvel_old,0,0,1,yvel_old.nGrowVect());
317  MultiFab::Copy(zvel_new,zvel_old,0,0,1,zvel_old.nGrowVect());
318 
319  bool fast_only = false;
320  bool vel_and_mom_synced = true;
321 
322  apply_bcs(state_old, old_time,
323  state_old[IntVars::cons].nGrow(), state_old[IntVars::xmom].nGrow(),
324  fast_only, vel_and_mom_synced);
325 
326  cons_to_prim(state_old[IntVars::cons], S_prim, state_old[IntVars::cons].nGrow());
327 
328  make_pi_stage(state_old[IntVars::cons]);
329 
330  // ***********************************************************************************************
331  // Define a new MultiFab that holds q_total and fill it by summing the moisture components --
332  // to be used in buoyancy calculation and as part of the inertial weighting in the
333  // ***********************************************************************************************
334 
335  const bool l_eb_terrain = (solverChoice.terrain_type == TerrainType::EB);
336  MultiFab qt(grids[level], dmap[level], 1, (l_eb_terrain) ? 2 : 1);
337  qt.setVal(0);
338 
339 #include "ERF_TI_no_substep_fun.H"
340 #include "ERF_TI_substep_fun.H"
341 #include "ERF_TI_slow_rhs_pre.H"
342 #include "ERF_TI_slow_rhs_post.H"
343 
344  // ***************************************************************************************
345  // Setup the integrator and integrate for a single timestep
346  // **************************************************************************************
347  MRISplitIntegrator<Vector<MultiFab> >& mri_integrator = *mri_integrator_mem[level];
348 
349  // Define rhs and 'post update' utility function that is called after calculating
350  // any state data (e.g. at RK stages or at the end of a timestep)
351  mri_integrator.set_slow_rhs_pre(slow_rhs_fun_pre);
352  mri_integrator.set_slow_rhs_post(slow_rhs_fun_post);
353 
356  mri_integrator.set_no_substep(no_substep_fun);
357 
358  mri_integrator.advance(state_old, state_new, old_time, dt_advance);
359 
360  if (verbose) Print() << "Done with advance_dycore at level " << level << std::endl;
361 }
void ComputeStrain_EB(const MFIter &mfi, Box bxcc, Box tbxxy, Box tbxxz, Box tbxyz, Box domain, const Array4< const Real > &u, const Array4< const Real > &v, const Array4< const Real > &w, Array4< Real > &tau11, Array4< Real > &tau22, Array4< Real > &tau33, Array4< Real > &tau12, Array4< Real > &tau13, Array4< Real > &tau23, const GpuArray< Real, AMREX_SPACEDIM > &dxInv, const BCRec *bc_ptr, const eb_ &ebfact, Array4< Real > &tau13i, Array4< Real > &tau23i)
Definition: ERF_ComputeStrain_EB.cpp:28
void ComputeStrain_N(Box bxcc, Box tbxxy, Box tbxxz, Box tbxyz, Box domain, const Array4< const Real > &u, const Array4< const Real > &v, const Array4< const Real > &w, Array4< Real > &tau11, Array4< Real > &tau22, Array4< Real > &tau33, Array4< Real > &tau12, Array4< Real > &tau13, Array4< Real > &tau23, const GpuArray< Real, AMREX_SPACEDIM > &dxInv, const Array4< const Real > &mf_mx, const Array4< const Real > &mf_ux, const Array4< const Real > &mf_vx, const Array4< const Real > &mf_my, const Array4< const Real > &mf_uy, const Array4< const Real > &mf_vy, const BCRec *bc_ptr, Array4< Real > &tau13i, Array4< Real > &tau23i)
Definition: ERF_ComputeStrain_N.cpp:31
void ComputeStrain_S(Box bxcc, Box tbxxy, Box tbxxz, Box tbxyz, Box domain, const Array4< const Real > &u, const Array4< const Real > &v, const Array4< const Real > &w, Array4< Real > &tau11, Array4< Real > &tau22, Array4< Real > &tau33, Array4< Real > &tau12, Array4< Real > &tau21, Array4< Real > &tau13, Array4< Real > &tau31, Array4< Real > &tau23, Array4< Real > &tau32, const Gpu::DeviceVector< Real > &stretched_dz_d, const GpuArray< Real, AMREX_SPACEDIM > &dxInv, const Array4< const Real > &mf_mx, const Array4< const Real > &mf_ux, const Array4< const Real > &mf_vx, const Array4< const Real > &mf_my, const Array4< const Real > &mf_uy, const Array4< const Real > &mf_vy, const BCRec *bc_ptr, Array4< Real > &tau13i, Array4< Real > &tau23i)
Definition: ERF_ComputeStrain_S.cpp:39
void ComputeStrain_T(Box bxcc, Box tbxxy, Box tbxxz, Box tbxyz, Box domain, const Array4< const Real > &u, const Array4< const Real > &v, const Array4< const Real > &w, Array4< Real > &tau11, Array4< Real > &tau22, Array4< Real > &tau33, Array4< Real > &tau12, Array4< Real > &tau21, Array4< Real > &tau13, Array4< Real > &tau31, Array4< Real > &tau23, Array4< Real > &tau32, const Array4< const Real > &z_nd, const Array4< const Real > &detJ, const GpuArray< Real, AMREX_SPACEDIM > &dxInv, const Array4< const Real > &mf_mx, const Array4< const Real > &mf_ux, const Array4< const Real > &mf_vx, const Array4< const Real > &mf_my, const Array4< const Real > &mf_uy, const Array4< const Real > &mf_vy, const BCRec *bc_ptr, Array4< Real > &tau13i, Array4< Real > &tau23i)
Definition: ERF_ComputeStrain_T.cpp:39
void ComputeTurbulentViscosity(Real dt, const MultiFab &xvel, const MultiFab &yvel, Vector< std::unique_ptr< MultiFab >> &Tau_lev, MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, MultiFab &Diss, const Geometry &geom, Vector< std::unique_ptr< MultiFab >> &mapfac, const std::unique_ptr< MultiFab > &z_phys_nd, const SolverChoice &solverChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, const MultiFab *z_0, const bool &use_terrain_fitted_coords, const bool &use_moisture, int level, const BCRec *bc_ptr, const eb_ &ebfact, bool vert_only)
Definition: ERF_ComputeTurbulentViscosity.cpp:791
@ tau12
Definition: ERF_DataStruct.H:32
@ tau33
Definition: ERF_DataStruct.H:32
@ tau22
Definition: ERF_DataStruct.H:32
@ tau11
Definition: ERF_DataStruct.H:32
@ tau32
Definition: ERF_DataStruct.H:32
@ tau31
Definition: ERF_DataStruct.H:32
@ tau21
Definition: ERF_DataStruct.H:32
@ ubar
Definition: ERF_DataStruct.H:98
@ wbar
Definition: ERF_DataStruct.H:98
@ vbar
Definition: ERF_DataStruct.H:98
@ thetabar
Definition: ERF_DataStruct.H:98
@ nvars_sponge
Definition: ERF_DataStruct.H:103
@ vbar_sponge
Definition: ERF_DataStruct.H:103
@ ubar_sponge
Definition: ERF_DataStruct.H:103
@ v_x
Definition: ERF_DataStruct.H:24
@ u_y
Definition: ERF_DataStruct.H:25
@ v_y
Definition: ERF_DataStruct.H:25
@ m_y
Definition: ERF_DataStruct.H:25
@ u_x
Definition: ERF_DataStruct.H:24
@ m_x
Definition: ERF_DataStruct.H:24
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
auto no_substep_fun
Definition: ERF_TI_no_substep_fun.H:4
auto slow_rhs_fun_post
Definition: ERF_TI_slow_rhs_post.H:3
auto slow_rhs_fun_pre
Definition: ERF_TI_slow_rhs_pre.H:6
auto acoustic_substepping_fun
Definition: ERF_TI_substep_fun.H:6
auto make_pi_stage
Definition: ERF_TI_utils.H:4
auto apply_bcs
Definition: ERF_TI_utils.H:34
Real z_0
Definition: ERF_UpdateWSubsidence_Bomex.H:10
void cons_to_prim(const MultiFab &cons_state, MultiFab &S_prim, int ng)
Definition: ERF_Utils.cpp:6
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > mapfac
Definition: ERF.H:1056
amrex::Vector< std::unique_ptr< MRISplitIntegrator< amrex::Vector< amrex::MultiFab > > > > mri_integrator_mem
Definition: ERF.H:914
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_sinesq_stag_ptrs
Definition: ERF.H:1428
amrex::Vector< amrex::Vector< amrex::Real > > h_w_subsid
Definition: ERF.H:1396
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc
Definition: ERF.H:1032
amrex::Vector< std::unique_ptr< amrex::MultiFab > > hfx3_EB
Definition: ERF.H:1022
amrex::Vector< amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > > d_sponge_ptrs
Definition: ERF.H:1424
amrex::Vector< long > dt_mri_ratio
Definition: ERF.H:899
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q2fx3_lev
Definition: ERF.H:1017
std::unique_ptr< ProblemBase > prob
Definition: ERF.H:886
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > stretched_dz_d
Definition: ERF.H:1062
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_diss_lev
Definition: ERF.H:1015
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_sinesq_ptrs
Definition: ERF.H:1427
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_v_geos
Definition: ERF.H:1403
amrex::Vector< amrex::Vector< amrex::Real > > h_v_geos
Definition: ERF.H:1402
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rhotheta_src
Definition: ERF.H:1393
amrex::Vector< amrex::Vector< amrex::Real > > h_u_geos
Definition: ERF.H:1399
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SmnSmn_lev
Definition: ERF.H:1001
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rhoqt_src
Definition: ERF.H:1394
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_u_geos
Definition: ERF.H:1400
static int fixed_mri_dt_ratio
Definition: ERF.H:1159
amrex::Vector< amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > > d_rayleigh_ptrs
Definition: ERF.H:1423
Definition: ERF_MRI.H:16
void set_acoustic_substepping(std::function< void(int, int, int, T &, const T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const amrex::Real, const amrex::Real)> F)
Definition: ERF_MRI.H:143
void set_no_substep(std::function< void(T &, T &, T &, amrex::Real, amrex::Real, int)> F)
Definition: ERF_MRI.H:161
void set_slow_rhs_post(std::function< void(T &, T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
Definition: ERF_MRI.H:138
void set_slow_rhs_pre(std::function< void(T &, T &, T &, const amrex::Real, const amrex::Real, const amrex::Real, const int)> F)
Definition: ERF_MRI.H:134
void set_slow_fast_timestep_ratio(const int timestep_ratio=1)
Definition: ERF_MRI.H:151
amrex::Real advance(T &S_old, T &S_new, amrex::Real time, const amrex::Real time_step)
Definition: ERF_MRI.H:171
@ pi0_comp
Definition: ERF_IndexDefines.H:75
@ p0_comp
Definition: ERF_IndexDefines.H:74
@ r0_comp
Definition: ERF_IndexDefines.H:73
@ qt
Definition: ERF_Kessler.H:28
real(c_double), parameter p0
Definition: ERF_module_model_constants.F90:40
real(kind=kind_phys), parameter, private r0
Definition: ERF_module_mp_wsm6.F90:21
bool rayleigh_damp_V
Definition: ERF_DampingStruct.H:85
bool rayleigh_damp_T
Definition: ERF_DampingStruct.H:87
bool rayleigh_damp_W
Definition: ERF_DampingStruct.H:86
bool rayleigh_damp_U
Definition: ERF_DampingStruct.H:84
Definition: ERF_DiffStruct.H:19
MolecDiffType molec_diff_type
Definition: ERF_DiffStruct.H:84
static MeshType mesh_type
Definition: ERF_DataStruct.H:1211
DampingChoice dampingChoice
Definition: ERF_DataStruct.H:1221
DiffChoice diffChoice
Definition: ERF_DataStruct.H:1220
bool custom_rhotheta_forcing
Definition: ERF_DataStruct.H:1314
bool custom_geostrophic_profile
Definition: ERF_DataStruct.H:1319
bool use_num_diff
Definition: ERF_DataStruct.H:1385
bool custom_moisture_forcing
Definition: ERF_DataStruct.H:1315
static SpongeType sponge_type
Definition: ERF_SpongeStruct.H:90
Definition: ERF_TurbStruct.H:82
bool use_kturb
Definition: ERF_TurbStruct.H:513
Here is the call graph for this function:

◆ advance_lsm()

void ERF::advance_lsm ( int  lev,
amrex::MultiFab &  cons_in,
amrex::MultiFab &  xvel_in,
amrex::MultiFab &  yvel_in,
const amrex::Real time,
const amrex::Real dt_advance 
)
11 {
12  if (solverChoice.lsm_type != LandSurfaceType::None) {
13  if (solverChoice.lsm_type == LandSurfaceType::NOAHMP) {
14  lsm.Advance(lev, cons_in, xvel_in, yvel_in,
15  SFS_hfx3_lev[lev].get(), SFS_q1fx3_lev[lev].get(),
16  time, dt_advance, istep[0]);
17  } else {
18  lsm.Advance(lev, dt_advance);
19  }
20  }
21 }
LandSurface lsm
Definition: ERF.H:965
amrex::Vector< int > istep
Definition: ERF.H:892
void Advance(const int &lev, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, amrex::MultiFab *hfx3_out, amrex::MultiFab *qfx3_out, const amrex::Real &time, const amrex::Real &dt_advance, const int &nstep)
Definition: ERF_LandSurface.H:53
LandSurfaceType lsm_type
Definition: ERF_DataStruct.H:1392
Here is the call graph for this function:

◆ advance_microphysics()

void ERF::advance_microphysics ( int  lev,
amrex::MultiFab &  cons_in,
const amrex::Real dt_advance,
const int &  iteration,
const amrex::Real time 
)
10 {
11  if (solverChoice.moisture_type != MoistureType::None) {
12  if (lev > 0) {
13  MultiFab& U_new = vars_new[lev][Vars::xvel];
14  MultiFab& V_new = vars_new[lev][Vars::yvel];
15  MultiFab& W_new = vars_new[lev][Vars::zvel];
16  FillPatchFineLevel(lev, time + dt_advance,
17  {&cons, &U_new, &V_new, &W_new},
18  {&cons, &rU_new[lev], &rV_new[lev], &rW_new[lev]},
19  base_state[lev], base_state[lev]);
20  } else {
21  cons.FillBoundary(geom[lev].periodicity());
22  }
23  micro->Update_Micro_Vars_Lev(lev, cons);
24  micro->Advance(lev, dt_advance, iteration, time, solverChoice, vars_new, z_phys_nd, phys_bc_type);
25  micro->Update_State_Vars_Lev(lev, cons, *z_phys_nd[lev]);
26 
27  // Sync cons[lev-1] covered cells with the moist state just written
28  // to cons[lev]. Without this, the next sub-cycle's FillPatchFineLevel
29  // for level lev would pull stale (latent-heat-less) values from
30  // cons[lev-1]'s coarse cells via cell-conservative interpolation,
31  // causing an artificial outward heat/q flux across the lev/(lev-1)
32  // boundary that drains the bubble interior.
33  if (lev > 0 && solverChoice.coupling_type == CouplingType::TwoWay &&
34  Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
35  AverageDownMoistStateTo(lev - 1);
36  }
37  }
38 }
void AverageDownMoistStateTo(int crse_lev)
Conservation-preserving fine→coarse average of moist state components (RhoTheta and the contiguous mo...
Definition: ERF_AverageDown.cpp:243
std::unique_ptr< Microphysics > micro
Definition: ERF.H:949
static MoistureModelType modelType(const MoistureType a_moisture_type)
query if a specified moisture model is Eulerian or Lagrangian
Definition: ERF_Microphysics.H:102
CouplingType coupling_type
Definition: ERF_DataStruct.H:1388
Here is the call graph for this function:

◆ advance_radiation()

void ERF::advance_radiation ( int  lev,
amrex::MultiFab &  cons_in,
const amrex::Real dt_advance 
)
8 {
9  if (solverChoice.rad_type != RadiationType::None) {
10 #ifdef ERF_USE_NETCDF
11  MultiFab *lat_ptr = lat_m[lev].get();
12  MultiFab *lon_ptr = lon_m[lev].get();
13 #else
14  MultiFab *lat_ptr = nullptr;
15  MultiFab *lon_ptr = nullptr;
16 #endif
17  // T surf from SurfaceLayer if we have it
18  MultiFab* t_surf = (m_SurfaceLayer) ? m_SurfaceLayer->get_t_surf(lev) : nullptr;
19 
20  // RRTMGP inputs names and pointers
21  Vector<std::string> lsm_input_names = rad[lev]->get_lsm_input_varnames();
22  Vector<MultiFab*> lsm_input_ptrs(lsm_input_names.size(),nullptr);
23  for (int i(0); i<lsm_input_ptrs.size(); ++i) {
24  int varIdx = lsm.Get_DataIdx(lev,lsm_input_names[i]);
25  if (varIdx >= 0) { lsm_input_ptrs[i] = lsm.Get_Data_Ptr(lev,varIdx); }
26  }
27 
28  // RRTMGP output names and pointers
29  Vector<std::string> lsm_output_names = rad[lev]->get_lsm_output_varnames();
30  Vector<MultiFab*> lsm_output_ptrs(lsm_output_names.size(),nullptr);
31  for (int i(0); i<lsm_output_ptrs.size(); ++i) {
32  int varIdx = lsm.Get_DataIdx(lev,lsm_output_names[i]);
33  if (varIdx >= 0) { lsm_output_ptrs[i] = lsm.Get_Data_Ptr(lev,varIdx); }
34  }
35 
36  // Enter radiation class driver
37  amrex::Real time_for_rad = t_old[lev] + start_time;
38  rad[lev]->Run(lev, istep[lev], time_for_rad, dt_advance,
39  cons.boxArray(), geom[lev], &(cons),
40  lmask_lev[lev][0].get(), t_surf,
41  lsm_input_ptrs, lsm_output_ptrs,
42  qheating_rates[lev].get(), rad_fluxes[lev].get(),
43  z_phys_nd[lev].get() , lat_ptr, lon_ptr);
44  }
45 }
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > lmask_lev
Definition: ERF.H:1006
amrex::Vector< std::unique_ptr< IRadiation > > rad
Definition: ERF.H:971
amrex::Vector< amrex::Real > t_old
Definition: ERF.H:897
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lon_m
Definition: ERF.H:849
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lat_m
Definition: ERF.H:849
amrex::Vector< std::unique_ptr< amrex::MultiFab > > qheating_rates
Definition: ERF.H:972
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rad_fluxes
Definition: ERF.H:973
int Get_DataIdx(const int &lev, std::string &varname)
Definition: ERF_LandSurface.H:110
amrex::MultiFab * Get_Data_Ptr(const int &lev, const int &varIdx)
Definition: ERF_LandSurface.H:92
@ t_surf
Definition: ERF_OceanSurf.H:14
RadiationType rad_type
Definition: ERF_DataStruct.H:1393

◆ appendPlotVariables()

void ERF::appendPlotVariables ( const std::string &  pp_plot_var_names,
amrex::Vector< std::string > &  plot_var_names 
)
private
177 {
178  ParmParse pp(pp_prefix);
179 
180  Vector<std::string> plot_var_names(0);
181  if (pp.contains(pp_plot_var_names.c_str())) {
182  std::string nm;
183  int nPltVars = pp.countval(pp_plot_var_names.c_str());
184  for (int i = 0; i < nPltVars; i++) {
185  pp.get(pp_plot_var_names.c_str(), nm, i);
186  // Add the named variable to our list of plot variables
187  // if it is not already in the list
188  if (!containerHasElement(plot_var_names, nm)) {
189  plot_var_names.push_back(nm);
190  }
191  }
192  }
193 
194  Vector<std::string> tmp_plot_names(0);
195 #ifdef ERF_USE_PARTICLES
196  Vector<std::string> particle_mesh_plot_names;
197  particleData.GetMeshPlotVarNames( particle_mesh_plot_names );
198  if (particle_mesh_plot_names.size() > 0) {
199  static bool first_call = true;
200  if (first_call) {
201  Print() << "ParticleData: the following additional Eulerian variables are available to plot:\n";
202  for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
203  Print() << " " << particle_mesh_plot_names[i] << "\n";
204  }
205  first_call = false;
206  }
207  for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
208  std::string tmp(particle_mesh_plot_names[i]);
209  if (containerHasElement(plot_var_names, tmp) ) {
210  tmp_plot_names.push_back(tmp);
211  }
212  }
213  }
214 #endif
215 
216  {
217  Vector<std::string> microphysics_plot_names;
218  micro->GetPlotVarNames(microphysics_plot_names);
219  if (microphysics_plot_names.size() > 0) {
220  static bool first_call = true;
221  if (first_call) {
222  Print() << getEnumNameString(solverChoice.moisture_type)
223  << ": the following additional variables are available to plot:\n";
224  for (int i = 0; i < microphysics_plot_names.size(); i++) {
225  Print() << " " << microphysics_plot_names[i] << "\n";
226  }
227  first_call = false;
228  }
229  for (auto& plot_name : microphysics_plot_names) {
230  if (containerHasElement(plot_var_names, plot_name)) {
231  tmp_plot_names.push_back(plot_name);
232  }
233  }
234  }
235  }
236 
237  for (int i = 0; i < tmp_plot_names.size(); i++) {
238  a_plot_var_names.push_back( tmp_plot_names[i] );
239  }
240 
241  // Finally, check to see if we found all the requested variables
242  for (const auto& plot_name : plot_var_names) {
243  if (!containerHasElement(a_plot_var_names, plot_name)) {
244  if (amrex::ParallelDescriptor::IOProcessor()) {
245  Warning("\nWARNING: Requested to plot variable '" + plot_name + "' but it is not available");
246  }
247  }
248  }
249 }
bool containerHasElement(const V &iterable, const T &query)
Definition: ERF_Container.H:5
std::string pp_prefix
Definition: ERF.H:619
@ tmp
Definition: ERF_AdvanceWSM6.cpp:114
Here is the call graph for this function:

◆ apply_gaussian_smoothing_to_perturbations()

void ERF::apply_gaussian_smoothing_to_perturbations ( const int  lev,
amrex::MultiFab &  mf_cc_pert 
)
88 {
89  const Geometry& gm = geom[lev];
90  const Real dx = gm.CellSize(0);
91  const Real dy = gm.CellSize(1);
92 
93  const Real dmesh = std::min(dx, dy);
94 
95  // ---- User choice ----
97  const int r = static_cast<int>(3.0 * sigma / dmesh);
98 
99  const int ncomp = mf_cc_pert.nComp();
100 
101  // ---- Precompute Gaussian weights ----
102  const int wsize = 2*r + 1;
103  Vector<Real> w_host(wsize * wsize);
104 
105  Real Z = zero;
106  for (int m = -r; m <= r; ++m) {
107  for (int n = -r; n <= r; ++n) {
108  Real val = std::exp(-(m*m*dx*dx + n*n*dy*dy)
109  /(two*sigma*sigma));
110  w_host[(m+r)*wsize + (n+r)] = val;
111  Z += val;
112  }
113  }
114 
115  for (auto& v : w_host) {
116  v /= Z;
117  }
118 
119  Gpu::DeviceVector<Real> w_dev(w_host.size());
120  Gpu::copy(Gpu::hostToDevice, w_host.begin(), w_host.end(), w_dev.begin());
121 
122  Real const* w = w_dev.data();
123 
124  // ---- Create a grown copy (for stencil access) ----
125  IntVect ngrow_big(AMREX_D_DECL(r, r, 0));
126 
127  MultiFab mf_copy(mf_cc_pert.boxArray(),
128  mf_cc_pert.DistributionMap(),
129  ncomp, ngrow_big);
130 
131  mf_copy.ParallelCopy(mf_cc_pert,
132  0, 0, ncomp,
133  IntVect(0), ngrow_big,
134  gm.periodicity());
135 
136  // ---- Apply smoothing ----
137  for (MFIter mfi(mf_cc_pert, TilingIfNotGPU()); mfi.isValid(); ++mfi)
138  {
139  const Box& bx = mfi.tilebox();
140 
141  auto const& in = mf_copy.const_array(mfi);
142  auto const& out = mf_cc_pert.array(mfi);
143 
144  ParallelFor(bx, ncomp,
145  [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
146  {
147  Real sum = zero;
148 
149  for (int m = -r; m <= r; ++m) {
150  for (int nn = -r; nn <= r; ++nn) {
151  Real wij = w[(m+r)*wsize + (nn+r)];
152  sum += wij * in(i+m, j+nn, k, n);
153  }
154  }
155 
156  out(i,j,k,n) = sum;
157  });
158  }
160 }
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
struct @28 out
struct @28 in
const Real dy
Definition: ERF_InitCustomPert_ABL.H:24
const Real dx
Definition: ERF_InitCustomPert_ABL.H:23
amrex::Real sigma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:11
void NormalizeMultiFabRMS_PerComponent(MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:40
ParallelFor(grown_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);})
amrex::Real ens_pert_correlated_radius
Definition: ERF_DataStruct.H:1448
Here is the call graph for this function:

◆ ApplyOceanSurfaceState()

void ERF::ApplyOceanSurfaceState ( const amrex::Vector< amrex::MultiFab * > &  state,
amrex::Real  time 
)
260 {
261  if (solverChoice.lsm_type != LandSurfaceType::OceanSurf) {
262  return;
263  }
264 
265  if (!state.empty() && state[0] != nullptr && lsm.Get_Data_Ptr(0, 0) != nullptr) {
266  auto* dst = lsm.Get_Data_Ptr(0, 0);
267  const auto lsm_geom = lsm.Get_Lsm_Geom(0);
268  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
269  lsm_geom.isPeriodic(0) == Geom(0).isPeriodic(0) &&
270  lsm_geom.isPeriodic(1) == Geom(0).isPeriodic(1) &&
271  lsm_geom.isPeriodic(2) == Geom(0).isPeriodic(2),
272  "OceanSurf t_surf geometry lost ERF periodic flags.");
273  const int dst_k = dst->boxArray().minimalBox().smallEnd(2);
274  const int src_k = state[0]->boxArray().minimalBox().bigEnd(2);
275  for (amrex::MFIter mfi(*dst, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
276  amrex::Box bx = amrex::makeSlab(mfi.validbox(), 2, dst_k);
277  auto dst_arr = dst->array(mfi);
278  auto src_arr = state[0]->const_array(mfi);
279  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int) {
280  dst_arr(i,j,dst_k) = src_arr(i,j,src_k);
281  });
282  }
283  dst->FillBoundary(lsm_geom.periodicity());
284  amrex::Gpu::streamSynchronize();
285 
286  const amrex::Real src_min = state[0]->min(0);
287  const amrex::Real src_max = state[0]->max(0);
288  const amrex::Real dst_min = dst->min(0);
289  const amrex::Real dst_max = dst->max(0);
290 
291  if (amrex::ParallelDescriptor::IOProcessor()) {
292  amrex::Print() << "OceanSurf apply at t=" << time
293  << " s from SST slab: source min/max = "
294  << src_min << " / " << src_max
295  << " K, cache min/max = "
296  << dst_min << " / " << dst_max
297  << " K" << std::endl;
298  }
299 
300  if (dst_min < amrex::Real(260.0) || dst_max > amrex::Real(320.0)) {
301  amrex::Warning("OceanSurf t_surf is outside the expected [260, 320] K range");
302  }
303  }
304 }
amrex::Geometry Get_Lsm_Geom(const int &lev)
Definition: ERF_LandSurface.H:98
Here is the call graph for this function:

◆ AverageDown()

void ERF::AverageDown ( )
private
17 {
18  AMREX_ALWAYS_ASSERT(solverChoice.coupling_type == CouplingType::TwoWay);
19 
20  int src_comp, num_comp;
21  for (int lev = finest_level-1; lev >= 0; --lev)
22  {
23  // If anelastic we don't average down rho because rho == rho0.
24  if (solverChoice.anelastic[lev]) {
25  src_comp = 1;
26  } else {
27  src_comp = 0;
28  }
29  num_comp = vars_new[0][Vars::cons].nComp() - src_comp;
30  AverageDownTo(lev,src_comp,num_comp);
31  }
32 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
void AverageDownTo(int crse_lev, int scomp, int ncomp, bool do_perturbational_and_momenta=true)
Definition: ERF_AverageDown.cpp:36
amrex::Vector< int > anelastic
Definition: ERF_DataStruct.H:1229
Here is the call graph for this function:

◆ AverageDownMoistStateTo()

void ERF::AverageDownMoistStateTo ( int  crse_lev)

Conservation-preserving fine→coarse average of moist state components (RhoTheta and the contiguous moist q range) using detJ/mfac weighting, reusing AverageDownTo with perturbational/momenta handling disabled.

244 {
245  AMREX_ALWAYS_ASSERT(solverChoice.coupling_type == CouplingType::TwoWay);
246  AMREX_ALWAYS_ASSERT(crse_lev >= 0 && crse_lev < finest_level);
247 
248  AverageDownTo(crse_lev, RhoTheta_comp, 1, false);
249 
250  const int n_moist = (micro) ? micro->Get_Qstate_Size() : 0;
251  if (n_moist > 0) {
252  AverageDownTo(crse_lev, RhoQ1_comp, n_moist, false);
253  }
254 }
Here is the call graph for this function:

◆ AverageDownTo()

void ERF::AverageDownTo ( int  crse_lev,
int  scomp,
int  ncomp,
bool  do_perturbational_and_momenta = true 
)
37 {
38  if (do_perturbational_and_momenta) {
39  if (solverChoice.anelastic[crse_lev]) {
40  AMREX_ALWAYS_ASSERT(scomp == 1);
41  } else {
42  AMREX_ALWAYS_ASSERT(scomp == 0);
43  }
44  AMREX_ALWAYS_ASSERT(ncomp == vars_new[crse_lev][Vars::cons].nComp() - scomp);
45  } else {
46  AMREX_ALWAYS_ASSERT(scomp >= 0);
47  AMREX_ALWAYS_ASSERT(ncomp > 0);
48  AMREX_ALWAYS_ASSERT(scomp + ncomp <= vars_new[crse_lev][Vars::cons].nComp());
49  }
50  AMREX_ALWAYS_ASSERT(solverChoice.coupling_type == CouplingType::TwoWay);
51 
52  // ******************************************************************************************
53  // First do cell-centered quantities
54  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
55  // m is the map scale factor at cell centers
56  // Here we multiply (rho S) by detJ and divide (rho S) by m^2 before average down
57  // ******************************************************************************************
58  for (int lev = crse_lev; lev <= crse_lev+1; lev++) {
59  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
60  const Box& bx = mfi.tilebox();
61  const Array4< Real> cons_arr = vars_new[lev][Vars::cons].array(mfi);
62  const Array4<const Real> mfx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
63  const Array4<const Real> mfy_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
64  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
65  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
66  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
67  {
68  cons_arr(i,j,k,scomp+n) *= detJ_arr(i,j,k) / (mfx_arr(i,j,0)*mfy_arr(i,j,0));
69  });
70  } else {
71  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
72  {
73  cons_arr(i,j,k,scomp+n) /= (mfx_arr(i,j,0)*mfy_arr(i,j,0));
74  });
75  }
76  } // mfi
77  } // lev
78 
79  int fine_lev = crse_lev+1;
80 
81  if (do_perturbational_and_momenta && interpolation_type == StateInterpType::Perturbational) {
82  // Make the fine rho and (rho theta) be perturbational
83  MultiFab::Divide(vars_new[fine_lev][Vars::cons],vars_new[fine_lev][Vars::cons],
84  Rho_comp,RhoTheta_comp,1,IntVect{0});
85  MultiFab::Subtract(vars_new[fine_lev][Vars::cons],base_state[fine_lev],
86  BaseState::r0_comp,Rho_comp,1,IntVect{0});
87  MultiFab::Subtract(vars_new[fine_lev][Vars::cons],base_state[fine_lev],
88  BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
89 
90  // Make the crse rho and (rho theta) be perturbational
91  MultiFab::Divide(vars_new[crse_lev][Vars::cons],vars_new[crse_lev][Vars::cons],
92  Rho_comp,RhoTheta_comp,1,IntVect{0});
93  MultiFab::Subtract(vars_new[crse_lev][Vars::cons],base_state[crse_lev],
94  BaseState::r0_comp,Rho_comp,1,IntVect{0});
95  MultiFab::Subtract(vars_new[crse_lev][Vars::cons],base_state[crse_lev],
96  BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
97  }
98 
99  if (SolverChoice::terrain_type != TerrainType::EB) {
100  average_down(vars_new[crse_lev+1][Vars::cons],vars_new[crse_lev ][Vars::cons],
101  scomp, ncomp, refRatio(crse_lev));
102  } else {
103  // const auto dx = geom[fine_lev].CellSize();
104  // Setting cell_vol to the exact value may cause round-off errors in volume average.
105  // const Real cell_vol = dx[0]*dx[1]*dx[2];
106  constexpr Real cell_vol = one;
107  const BoxArray& ba = vars_new[fine_lev][IntVars::cons].boxArray();
108  const DistributionMapping& dm = vars_new[fine_lev][IntVars::cons].DistributionMap();
109  MultiFab vol_fine(ba, dm, 1, 0);
110  vol_fine.setVal(cell_vol);
111  EB_average_down(vars_new[fine_lev][Vars::cons],vars_new[crse_lev][Vars::cons],
112  vol_fine, *detJ_cc[fine_lev],
113  scomp, ncomp, refRatio(crse_lev));
114  }
115 
116  if (do_perturbational_and_momenta && interpolation_type == StateInterpType::Perturbational) {
117  // Restore the fine data to what it was
118  MultiFab::Add(vars_new[fine_lev][Vars::cons],base_state[fine_lev],
119  BaseState::r0_comp,Rho_comp,1,IntVect{0});
120  MultiFab::Add(vars_new[fine_lev][Vars::cons],base_state[fine_lev],
121  BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
122  MultiFab::Multiply(vars_new[fine_lev][Vars::cons],vars_new[fine_lev][Vars::cons],
123  Rho_comp,RhoTheta_comp,1,IntVect{0});
124 
125  // Make the crse data be full values not perturbational
126  MultiFab::Add(vars_new[crse_lev][Vars::cons],base_state[crse_lev],
127  BaseState::r0_comp,Rho_comp,1,IntVect{0});
128  MultiFab::Add(vars_new[crse_lev][Vars::cons],base_state[crse_lev],
129  BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
130  MultiFab::Multiply(vars_new[crse_lev][Vars::cons],vars_new[crse_lev][Vars::cons],
131  Rho_comp,RhoTheta_comp,1,IntVect{0});
132  }
133 
134  vars_new[crse_lev][Vars::cons].FillBoundary(geom[crse_lev].periodicity());
135 
136  // ******************************************************************************************
137  // Here we multiply (rho S) by m^2 and divide by detJ after average down
138  // ******************************************************************************************
139  for (int lev = crse_lev; lev <= crse_lev+1; lev++) {
140  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
141  const Box& bx = mfi.tilebox();
142  const Array4< Real> cons_arr = vars_new[lev][Vars::cons].array(mfi);
143  const Array4<const Real> mfx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
144  const Array4<const Real> mfy_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
145  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
146  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
147  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
148  {
149  cons_arr(i,j,k,scomp+n) *= (mfx_arr(i,j,0)*mfy_arr(i,j,0)) / detJ_arr(i,j,k);
150  });
151  } else { // MeshType::ConstantDz
152  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
153  {
154  cons_arr(i,j,k,scomp+n) *= (mfx_arr(i,j,0)*mfy_arr(i,j,0));
155  });
156  }
157  } // mfi
158  } // lev
159 
160  // Fill EB covered cells by old values
161  // (This won't be needed because EB_average_down copies the covered value.)
162  if (SolverChoice::terrain_type == TerrainType::EB) {
163  for (int lev = crse_lev; lev <= crse_lev+1; lev++) {
164  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
165  const Box& bx = mfi.tilebox();
166  const Array4< Real> cons_new = vars_new[lev][Vars::cons].array(mfi);
167  const Array4<const Real> cons_old = vars_old[lev][Vars::cons].array(mfi);
168  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
169  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
170  {
171  if (detJ_arr(i,j,k) == zero) {
172  cons_new(i,j,k,scomp+n) = cons_old(i,j,k,scomp+n);
173  }
174  });
175  } // mfi
176  } // lev
177  }
178 
179  if (!do_perturbational_and_momenta) return;
180 
181  // ******************************************************************************************
182  // Now average down momenta.
183  // Note that vars_new holds velocities not momenta, but we want to do conservative
184  // averaging so we first convert to momentum, then average down, then convert
185  // back to velocities -- only on the valid region
186  // ******************************************************************************************
187  for (int lev = crse_lev; lev <= crse_lev+1; lev++)
188  {
189  // FillBoundary for density so we can go back and forth between velocity and momentum
190  vars_new[lev][Vars::cons].FillBoundary(geom[lev].periodicity());
191 
192  const MultiFab* c_vfrac = nullptr;
193  if (SolverChoice::terrain_type == TerrainType::EB) {
194  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
195  }
196 
197  VelocityToMomentum(vars_new[lev][Vars::xvel], IntVect(0,0,0),
198  vars_new[lev][Vars::yvel], IntVect(0,0,0),
199  vars_new[lev][Vars::zvel], IntVect(0,0,0),
200  vars_new[lev][Vars::cons],
201  rU_new[lev],
202  rV_new[lev],
203  rW_new[lev],
204  Geom(lev).Domain(),
206  c_vfrac);
207  }
208 
209  if (SolverChoice::terrain_type != TerrainType::EB) {
210  average_down_faces(rU_new[crse_lev+1], rU_new[crse_lev], refRatio(crse_lev), geom[crse_lev]);
211  average_down_faces(rV_new[crse_lev+1], rV_new[crse_lev], refRatio(crse_lev), geom[crse_lev]);
212  average_down_faces(rW_new[crse_lev+1], rW_new[crse_lev], refRatio(crse_lev), geom[crse_lev]);
213  } else {
214  EB_average_down_faces({&rU_new[crse_lev+1], &rV_new[crse_lev+1], &rW_new[crse_lev+1]},
215  {&rU_new[crse_lev], &rV_new[crse_lev], &rW_new[crse_lev]},
216  refRatio(crse_lev), 0);
217  }
218 
219  for (int lev = crse_lev; lev <= crse_lev+1; lev++) {
220 
221  const MultiFab* c_vfrac = nullptr;
222  if (SolverChoice::terrain_type == TerrainType::EB) {
223  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
224  }
225 
227  vars_new[lev][Vars::yvel],
228  vars_new[lev][Vars::zvel],
229  vars_new[lev][Vars::cons],
230  rU_new[lev],
231  rV_new[lev],
232  rW_new[lev],
233  Geom(lev).Domain(),
235  c_vfrac);
236  }
237 }
void MomentumToVelocity(MultiFab &xvel, MultiFab &yvel, MultiFab &zvel, const MultiFab &density, const MultiFab &xmom_in, const MultiFab &ymom_in, const MultiFab &zmom_in, const Box &domain, const Vector< BCRec > &domain_bcs_type_h, const MultiFab *c_vfrac)
Definition: ERF_MomentumToVelocity.cpp:25
static StateInterpType interpolation_type
Definition: ERF.H:1325
@ th0_comp
Definition: ERF_IndexDefines.H:76
Here is the call graph for this function:

◆ build_fine_mask()

void ERF::build_fine_mask ( int  lev,
amrex::MultiFab &  fine_mask 
)

Helper function for constructing a fine mask, that is, a MultiFab masking coarser data at a lower level by zeroing out covered cells in the fine mask MultiFab we compute.

Parameters
levelFine level index which masks underlying coarser data
126 {
127  // Mask for zeroing covered cells
128  AMREX_ASSERT(level > 0);
129 
130  BoxArray cba = grids[level-1];
131  DistributionMapping cdm = dmap[level-1];
132 
133  BoxArray fba = fine_mask_lev.boxArray();
134 
135  iMultiFab ifine_mask_lev = makeFineMask(cba, cdm, fba, ref_ratio[level-1], 1, 0);
136 
137  const auto fma = fine_mask_lev.arrays();
138  const auto ifma = ifine_mask_lev.arrays();
139  ParallelFor(fine_mask_lev, [=] AMREX_GPU_DEVICE(int bno, int i, int j, int k) noexcept
140  {
141  fma[bno](i,j,k) = ifma[bno](i,j,k);
142  });
143 }
Here is the call graph for this function:

◆ check_for_low_temp()

void ERF::check_for_low_temp ( amrex::MultiFab &  S)
2885 {
2886  // *****************************************************************************
2887  // Test for low temp (low is defined as beyond the microphysics range of validity)
2888  // *****************************************************************************
2889  //
2890  // This value is defined in erf_dtesati in Source/Utils/ERF_MicrophysicsUtils.H
2891  Real t_low = Real(273.16) - Real(85.);
2892  //
2893  for (MFIter mfi(S); mfi.isValid(); ++mfi)
2894  {
2895  Box bx = mfi.tilebox();
2896  const Array4<Real> &s_arr = S.array(mfi);
2897  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
2898  {
2899  const Real rho = s_arr(i, j, k, Rho_comp);
2900  const Real rhotheta = s_arr(i, j, k, RhoTheta_comp);
2901  const Real qv = s_arr(i, j, k, RhoQ1_comp) / rho;
2902 
2903  Real temp = getTgivenRandRTh(rho, rhotheta, qv);
2904 
2905  if (temp < t_low) {
2906 #ifdef AMREX_USE_GPU
2907  AMREX_DEVICE_PRINTF("Temperature too low in cell: %d %d %d %e \n", i,j,k,temp);
2908 #else
2909  printf("Temperature too low in cell: %d %d %d \n", i,j,k);
2910  printf("Based on temp / rhotheta / rho / qv %e %e %e %e \n", temp,rhotheta,rho,qv);
2911 #endif
2912  Abort();
2913  }
2914  });
2915  }
2916 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
rho
Definition: ERF_InitCustomPert_Bubble.H:106
@ qv
Definition: ERF_Kessler.H:29
Here is the call graph for this function:

◆ check_for_negative_theta()

void ERF::check_for_negative_theta ( amrex::MultiFab &  S)
2920 {
2921  // *****************************************************************************
2922  // Test for negative (rho theta)
2923  // *****************************************************************************
2924  for (MFIter mfi(S); mfi.isValid(); ++mfi)
2925  {
2926  Box bx = mfi.tilebox();
2927  const Array4<Real> &s_arr = S.array(mfi);
2928  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
2929  {
2930  const Real rho = s_arr(i, j, k, Rho_comp);
2931  const Real rhotheta = s_arr(i, j, k, RhoTheta_comp);
2932 
2933  if (rho <= zero) {
2934 #ifdef AMREX_USE_GPU
2935  AMREX_DEVICE_PRINTF("Rho is negative at %d %d %d %e \n", i,j,k,rho);
2936 #else
2937  printf("Rho is negative at %d %d %d %e \n", i,j,k,rho);
2938  Abort("Bad rho in check_for_negative_theta");
2939 #endif
2940  }
2941 
2942  if (rhotheta <= zero) {
2943 #ifdef AMREX_USE_GPU
2944  AMREX_DEVICE_PRINTF("RhoTheta is negative at %d %d %d %e \n", i,j,k,rhotheta);
2945 #else
2946  printf("RhoTheta is negative at %d %d %d %e \n", i,j,k,rhotheta);
2947  Abort("Bad theta in check_for_negative_theta");
2948 #endif
2949  }
2950 
2951  });
2952  } // mfi
2953 }
Here is the call graph for this function:

◆ check_mesh_type()

void ERF::check_mesh_type ( int  lev)
2958 {
2959  if (SolverChoice::mesh_type == MeshType::VariableDz) {
2960  MultiFab z_slab(convert(ba2d[lev],IntVect(1,1,1)),dmap[lev],1,0);
2961  int klo = geom[lev].Domain().smallEnd(2);
2962  for (MFIter mfi(z_slab); mfi.isValid(); ++mfi) {
2963  Box nbx = mfi.tilebox();
2964  Array4<Real const> const& z_arr = z_phys_nd[lev]->const_array(mfi);
2965  Array4<Real > const& z_slab_arr = z_slab.array(mfi);
2966  ParallelFor(nbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
2967  {
2968  z_slab_arr(i,j,k) = z_arr(i,j,klo);
2969  });
2970  }
2971  Real z_min = z_slab.min(0);
2972  Real z_max = z_slab.max(0);
2973 
2974  auto dz = geom[lev].CellSize()[2];
2975  if (z_max - z_min < Real(1.e-8) * dz) {
2976  SolverChoice::set_mesh_type(MeshType::StretchedDz);
2977  if (verbose > 0) {
2978  amrex::Print() << "Resetting mesh type to StretchedDz since terrain is flat" << std::endl;
2979  }
2980  }
2981  }
2982 }
amrex::Vector< amrex::BoxArray > ba2d
Definition: ERF.H:1361
@ dz
Definition: ERF_AdvanceWSM6.cpp:104
static void set_mesh_type(MeshType new_mesh_type)
Definition: ERF_DataStruct.H:1214
Here is the call graph for this function:

◆ check_state_for_nans()

void ERF::check_state_for_nans ( amrex::MultiFab const &  S)
2791 {
2792  amrex::Gpu::DeviceScalar<int> d_found(0);
2793 
2794  // comp, i, j, k
2795  amrex::Gpu::DeviceVector<int> d_info(4, -1);
2796 
2797  for (MFIter mfi(S,TilingIfNotGPU()); mfi.isValid(); ++mfi)
2798  {
2799  const Box& bx = mfi.tilebox();
2800  auto const& s_arr = S.const_array(mfi);
2801 
2802  const int ncomp = S.nComp();
2803 
2804  int* found = d_found.dataPtr();
2805  int* info = d_info.dataPtr();
2806 
2807  ParallelFor(bx,
2808  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
2809  {
2810  // Somebody already found a NaN
2811  if (*found) return;
2812 
2813  for (int n = 0; n < ncomp; ++n)
2814  {
2815  Real val = s_arr(i,j,k,n);
2816 
2817  if (val != val) // NaN test
2818  {
2819  // Only one thread wins
2820  if (amrex::Gpu::Atomic::CAS(found,0,1) == 0)
2821  {
2822  info[0] = n;
2823  info[1] = i;
2824  info[2] = j;
2825  info[3] = k;
2826  }
2827  return;
2828  }
2829  }
2830  });
2831  }
2832 
2833  amrex::Gpu::streamSynchronize();
2834 
2835  if (d_found.dataValue())
2836  {
2837  amrex::Vector<int> h_info(4);
2838  amrex::Print() << "Found flag = " << d_found.dataValue() << "\n";
2839 
2840  amrex::Gpu::copy(
2841  amrex::Gpu::deviceToHost,
2842  d_info.begin(),
2843  d_info.end(),
2844  h_info.begin());
2845 
2846  std::cout << "NaN found in component " << h_info[0]
2847  << " at (i,j,k) = ("
2848  << h_info[1] << ", "
2849  << h_info[2] << ", "
2850  << h_info[3] << ")\n";
2851 
2852  amrex::Abort("NaN detected in state");
2853  }
2854 }
Here is the call graph for this function:

◆ check_vels_for_nans()

void ERF::check_vels_for_nans ( amrex::MultiFab const &  xvel,
amrex::MultiFab const &  yvel,
amrex::MultiFab const &  zvel 
)
2858 {
2859  //
2860  // Test at the end of every full timestep whether the solution data contains NaNs
2861  //
2862  bool any_have_nans = false;
2863  if (xvel.contains_nan(0,1,0))
2864  {
2865  amrex::Print() << "x-velocity contains NaNs " << '\n';
2866  any_have_nans = true;
2867  }
2868  if (yvel.contains_nan(0,1,0))
2869  {
2870  amrex::Print() << "y-velocity contains NaNs" << '\n';
2871  any_have_nans = true;
2872  }
2873  if (zvel.contains_nan(0,1,0))
2874  {
2875  amrex::Print() << "z-velocity contains NaNs" << '\n';
2876  any_have_nans = true;
2877  }
2878  if (any_have_nans) {
2879  exit(0);
2880  }
2881 }

◆ ClearLevel()

void ERF::ClearLevel ( int  lev)
override
814 {
815  for (int var_idx = 0; var_idx < Vars::NumTypes; ++var_idx) {
816  vars_new[lev][var_idx].clear();
817  vars_old[lev][var_idx].clear();
818  }
819 
820  base_state[lev].clear();
821 
822  rU_new[lev].clear();
823  rU_old[lev].clear();
824  rV_new[lev].clear();
825  rV_old[lev].clear();
826  rW_new[lev].clear();
827  rW_old[lev].clear();
828 
829  if (lev > 0) {
830  zmom_crse_rhs[lev].clear();
831  }
832 
833  if ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) {
834  pp_inc[lev].clear();
835  }
836  if (solverChoice.anelastic[lev] == 0) {
837  lagged_delta_rt[lev].clear();
838  }
839  avg_xmom[lev].clear();
840  avg_ymom[lev].clear();
841  avg_zmom[lev].clear();
842 
843  // Clears the integrator memory
844  mri_integrator_mem[lev].reset();
845 
846  // Clears the physical boundary condition routines
847  physbcs_cons[lev].reset();
848  physbcs_u[lev].reset();
849  physbcs_v[lev].reset();
850  physbcs_w[lev].reset();
851  physbcs_base[lev].reset();
852 
853  // Clears the flux register array (only allocated for TwoWay coupling)
854  if (advflux_reg[lev]) {
855  advflux_reg[lev]->reset();
856  }
857 
858  // Clears the 2D arrays
859  if (sst_lev[lev][0]) {
860  for (int n = 0; n < sst_lev[lev].size(); n++) {
861  sst_lev[lev][n].reset();
862  }
863  }
864  if (tsk_lev[lev][0]) {
865  for (int n = 0; n < tsk_lev[lev].size(); n++) {
866  tsk_lev[lev][n].reset();
867  }
868  }
869  if (lat_m[lev]) {
870  lat_m[lev].reset();
871  }
872  if (lon_m[lev]) {
873  lon_m[lev].reset();
874  }
875  if (sinPhi_m[lev]) {
876  sinPhi_m[lev].reset();
877  }
878  if (cosPhi_m[lev]) {
879  cosPhi_m[lev].reset();
880  }
881 
882 #ifdef ERF_USE_FFT
883  // Clear any FFT solvers built at this level
884  if (m_3D_poisson.size() > lev) {
885  for (int n = 0; n < m_3D_poisson[lev].size(); n++) {
886  m_3D_poisson[lev][n].reset();
887  }
888  }
889  if (m_2D_poisson.size() > lev) {
890  for (int n = 0; n < m_2D_poisson[lev].size(); n++) {
891  m_2D_poisson[lev][n].reset();
892  }
893  }
894 #endif
895 }
amrex::Vector< amrex::MultiFab > avg_xmom
Definition: ERF.H:921
amrex::Vector< amrex::MultiFab > pp_inc
Definition: ERF.H:917
amrex::Vector< amrex::MultiFab > lagged_delta_rt
Definition: ERF.H:920
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > sst_lev
Definition: ERF.H:1004
amrex::Vector< amrex::YAFluxRegister * > advflux_reg
Definition: ERF.H:1075
amrex::Vector< std::unique_ptr< amrex::MultiFab > > sinPhi_m
Definition: ERF.H:851
amrex::Vector< std::unique_ptr< amrex::MultiFab > > cosPhi_m
Definition: ERF.H:851
amrex::Vector< amrex::MultiFab > avg_ymom
Definition: ERF.H:922
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_base > > physbcs_base
Definition: ERF.H:930
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > tsk_lev
Definition: ERF.H:1005
amrex::Vector< amrex::MultiFab > avg_zmom
Definition: ERF.H:923
@ NumTypes
Definition: ERF_IndexDefines.H:178
amrex::Vector< int > project_initial_velocity
Definition: ERF_DataStruct.H:1231

◆ cloud_fraction()

Real ERF::cloud_fraction ( double  time)
453 {
454  BL_PROFILE("ERF::cloud_fraction()");
455 
456  int lev = 0;
457  // This holds all of qc
458  MultiFab qc(vars_new[lev][Vars::cons],make_alias,RhoQ2_comp,1);
459 
460  int direction = 2; // z-direction
461  Box const& domain = geom[lev].Domain();
462 
463  auto const& qc_arr = qc.const_arrays();
464 
465  // qc_2d is an BaseFab<int> holding the max value over the column
466  auto qc_2d = ReduceToPlane<ReduceOpMax,int>(direction, domain, qc,
467  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) -> int
468  {
469  if (qc_arr[box_no](i,j,k) > 0) {
470  return 1;
471  } else {
472  return 0;
473  }
474  });
475 
476  auto* p = qc_2d.dataPtr();
477 
478  Long numpts = qc_2d.numPts();
479 
480  AMREX_ASSERT(numpts < Long(std::numeric_limits<int>::max));
481 
482 #if 1
483  if (ParallelDescriptor::UseGpuAwareMpi()) {
484  ParallelDescriptor::ReduceIntMax(p,static_cast<int>(numpts));
485  } else {
486  Gpu::PinnedVector<int> hv(numpts);
487  Gpu::copyAsync(Gpu::deviceToHost, p, p+numpts, hv.data());
488  Gpu::streamSynchronize();
489  ParallelDescriptor::ReduceIntMax(hv.data(),static_cast<int>(numpts));
490  Gpu::copyAsync(Gpu::hostToDevice, hv.data(), hv.data()+numpts, p);
491  }
492 
493  // Sum over component 0
494  Long num_cloudy = qc_2d.template sum<RunOn::Device>(0);
495 
496 #else
497  //
498  // We need this if we allow domain decomposition in the vertical
499  // but for now we leave it commented out
500  //
501  Long num_cloudy = Reduce::Sum<Long>(numpts,
502  [=] AMREX_GPU_DEVICE (Long i) -> Long {
503  if (p[i] == 1) {
504  return 1;
505  } else {
506  return 0;
507  }
508  });
509  ParallelDescriptor::ReduceLongSum(num_cloudy);
510 #endif
511 
512  Real num_total = Real(qc_2d.box().d_numPts());
513 
514  Real cloud_frac = num_cloudy / num_total;
515 
516  return cloud_frac;
517 }
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:43
@ qc
Definition: ERF_SatAdj.H:40
@ p
Definition: ERF_WSM6.H:176

Referenced by PackAtmosphericStates().

Here is the caller graph for this function:

◆ compute_divergence()

void ERF::compute_divergence ( int  lev,
amrex::MultiFab &  rhs,
amrex::Array< amrex::MultiFab const *, AMREX_SPACEDIM >  rho0_u_const,
amrex::Geometry const &  geom_at_lev 
)

Project the single-level velocity field to enforce incompressibility Note that the level may or may not be level zero

11 {
12  BL_PROFILE("ERF::compute_divergence()");
13 
14  auto dxInv = geom_at_lev.InvCellSizeArray();
15 
16  // ****************************************************************************
17  // Compute divergence which will form RHS
18  // Note that we replace "rho0w" with the contravariant momentum, Omega
19  // ****************************************************************************
20  if (solverChoice.terrain_type == TerrainType::EB)
21  {
22  bool already_on_centroids = true;
23  EB_computeDivergence(rhs, rho0_u_const, geom_at_lev, already_on_centroids);
24  }
25  else if (SolverChoice::mesh_type == MeshType::ConstantDz)
26  {
27  computeDivergence(rhs, rho0_u_const, geom_at_lev);
28  }
29  else
30  {
31  for ( MFIter mfi(rhs,TilingIfNotGPU()); mfi.isValid(); ++mfi)
32  {
33  Box bx = mfi.tilebox();
34  const Array4<Real const>& rho0u_arr = rho0_u_const[0]->const_array(mfi);
35  const Array4<Real const>& rho0v_arr = rho0_u_const[1]->const_array(mfi);
36  const Array4<Real const>& rho0w_arr = rho0_u_const[2]->const_array(mfi);
37  const Array4<Real >& rhs_arr = rhs.array(mfi);
38 
39  const Array4<Real const>& mf_mx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
40  const Array4<Real const>& mf_my = mapfac[lev][MapFacType::m_y]->const_array(mfi);
41  const Array4<Real const>& mf_vx = mapfac[lev][MapFacType::v_x]->const_array(mfi);
42  const Array4<Real const>& mf_uy = mapfac[lev][MapFacType::u_y]->const_array(mfi);
43 
44  if (SolverChoice::mesh_type == MeshType::StretchedDz)
45  {
46  Real* stretched_dz_d_ptr = stretched_dz_d[lev].data();
47  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
48  {
49  Real inv_dz = one/stretched_dz_d_ptr[k];
50  Real mfsq = mf_mx(i,j,0) * mf_my(i,j,0);
51  rhs_arr(i,j,k) = ( (rho0u_arr(i+1,j ,k )/mf_uy(i+1,j,0) - rho0u_arr(i,j,k)/mf_uy(i,j,0)) * dxInv[0]
52  +(rho0v_arr(i ,j+1,k )/mf_vx(i,j+1,0) - rho0v_arr(i,j,k)/mf_vx(i,j,0)) * dxInv[1]
53  +(rho0w_arr(i ,j ,k+1)/mfsq - rho0w_arr(i,j,k)/mfsq ) * inv_dz ) * mfsq;
54  });
55  }
56  else
57  {
58  //
59  // Note we compute the divergence using "rho0w" == Omega
60  //
61  const Array4<Real const>& ax_arr = ax[lev]->const_array(mfi);
62  const Array4<Real const>& ay_arr = ay[lev]->const_array(mfi);
63  const Array4<Real const>& dJ_arr = detJ_cc[lev]->const_array(mfi);
64 
65  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
66  {
67  Real mfsq = mf_mx(i,j,0) * mf_my(i,j,0);
68  rhs_arr(i,j,k) = ( ( ax_arr(i+1,j,k)*rho0u_arr(i+1,j,k)/mf_uy(i+1,j,0)
69  -ax_arr(i ,j,k)*rho0u_arr(i ,j,k)/mf_uy(i ,j,0) ) * dxInv[0]
70  + ( ay_arr(i,j+1,k)*rho0v_arr(i,j+1,k)/mf_vx(i,j+1,0)
71  -ay_arr(i,j ,k)*rho0v_arr(i,j ,k)/mf_vx(i,j ,0) ) * dxInv[1]
72  +( rho0w_arr(i,j,k+1)/mfsq
73  - rho0w_arr(i,j,k )/mfsq ) * dxInv[2] ) * mfsq / dJ_arr(i,j,k);
74  });
75  }
76  } // mfi
77  }
78 }
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ax
Definition: ERF.H:1033
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ay
Definition: ERF.H:1034
Here is the call graph for this function:

◆ compute_max_pressure_gradient_diagnostic()

void ERF::compute_max_pressure_gradient_diagnostic ( int  lev)
13 {
14  // We don't require HSE when anelastic because the pressure gradient
15  // is computed from the Poisson solve
16  if (solverChoice.anelastic[lev]) return;
17 
18  auto& lev_new = vars_new[lev];
19 
20  int ng = (solverChoice.terrain_type == TerrainType::EB) ? 3 : 1;
21 
22  const Real grav = solverChoice.gravity;
23 
24  Vector<MultiFab> gradp_temp; gradp_temp.resize(AMREX_SPACEDIM);
25  gradp_temp[0].define(vars_new[lev][Vars::xvel].boxArray(), lev_new[Vars::xvel].DistributionMap(), 1, 0);
26  gradp_temp[0].setVal(0.);
27  gradp_temp[1].define(vars_new[lev][Vars::yvel].boxArray(), lev_new[Vars::yvel].DistributionMap(), 1, 0);
28  gradp_temp[1].setVal(0.);
29  gradp_temp[2].define(vars_new[lev][Vars::zvel].boxArray(), lev_new[Vars::zvel].DistributionMap(), 1, 0);
30  gradp_temp[2].setVal(0.);
31 
32  int comp = 0;
33 
34  // Use this region to take max/min of gpx without including xlo,xhi if using real_bcs
35  Box xface_domain = surroundingNodes(geom[lev].Domain(), 0);
36  int ilo = xface_domain.smallEnd(0);
37  int ihi = xface_domain.bigEnd(0);
39  xface_domain.growLo(0,-1);
40  xface_domain.growHi(0,-1);
41  }
42 
43  // Use this region to take max/min of gpy without including ylo,yhi if using real_bcs
44  Box yface_domain = surroundingNodes(geom[lev].Domain(), 1);
45  int jlo = yface_domain.smallEnd(1);
46  int jhi = yface_domain.bigEnd(1);
48  yface_domain.growLo(1,-1);
49  yface_domain.growHi(1,-1);
50  }
51 
52 
53  // Use this region to take max/min of gpz without including top and bottom faces
54  Box zface_domain = surroundingNodes(geom[lev].Domain(), 2);
55  int klo = zface_domain.smallEnd(2);
56  int khi = zface_domain.bigEnd(2);
57 
58  zface_domain.growLo(2,-1);
59  zface_domain.growHi(2,-1);
60 
61  // *******************************************************************************
62  // First check that base state satisfies EOS
63  // *******************************************************************************
64 
65  Print() << " " << std::endl;
66 
67  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp , 1);
68  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp , 1);
69  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp , 1);
70  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
71 
72  MultiFab dp(p_hse.boxArray(), p_hse.DistributionMap(), 1, 0);
73 
74  // Initialize to zero in case of EB covered cells
75  dp.setVal(0.);
76 
77  for (MFIter mfi(dp); mfi.isValid(); ++mfi) {
78  Box bx = mfi.validbox();
79  auto const rhse_arr = r_hse.const_array(mfi);
80  auto const phse_arr = p_hse.const_array(mfi);
81  auto const qvhse_arr = qv_hse.const_array(mfi);
82  auto const thhse_arr = th_hse.const_array(mfi);
83  auto dpeos_arr = dp.array(mfi);
84 
85  if (solverChoice.terrain_type != TerrainType::EB) {
86  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
87  Real rhotheta = rhse_arr(i,j,k) * thhse_arr(i,j,k);
88  dpeos_arr(i,j,k) = std::abs(getPgivenRTh(rhotheta, qvhse_arr(i,j,k)) - phse_arr(i,j,k));
89  });
90  } else {
91  Array4<const Real> volfrac = (get_eb(lev).get_const_factory())->getVolFrac().const_array(mfi);
92  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
93  if (volfrac(i,j,k) > zero) {
94  Real rhotheta = rhse_arr(i,j,k) * thhse_arr(i,j,k);
95  dpeos_arr(i,j,k) = std::abs(getPgivenRTh(rhotheta, qvhse_arr(i,j,k)) - phse_arr(i,j,k));
96  }
97  });
98  }
99  }
100  Real max_diff = dp.max(0);
101  if (max_diff > 1.e-8) {
102  IntVect max_loc = dp.maxIndex(0);
103  Print() << "Max value of |p_hse - p_eos| is " << max_diff << std::endl;
104  Print() << " with max in cell " << max_loc << std::endl;
105  Abort("Base state violates EOS ");
106  } else {
107  Print() << "Max value of |p_hse - p_eos| is less than 1e-8" << std::endl;
108  }
109 
110  // *******************************************************************************
111  // Now compute pressure gradients for base state pressure
112  // *******************************************************************************
113 
114  compute_gradp(p_hse, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
115  get_eb(lev), gradp_temp, solverChoice);
116 
117  Real min_gpx = gradp_temp[0].min(xface_domain,comp);
118  Real max_gpx = gradp_temp[0].max(xface_domain,comp);
119  if (max_gpx != zero || min_gpx != zero) {
120  Print() << "Min/max value of dp0/dx are " << min_gpx << " " << max_gpx << std::endl;
121  IntVect min_loc = gradp_temp[0].minIndex(comp);
122  IntVect max_loc = gradp_temp[0].maxIndex(comp);
123  if (min_loc[0] != ilo && min_loc[0] != ihi) amrex::Print() << " with min at face " << min_loc;
124  if (max_loc[0] != ilo && max_loc[0] != ihi) amrex::Print() << " with max at face " << max_loc;
125  Print() << std::endl;
126  } else {
127  Print() << "Min/max value of dp0/dx are zero " << std::endl;
128  }
129 
130  Real min_gpy = gradp_temp[1].min(yface_domain,comp);
131  Real max_gpy = gradp_temp[1].max(yface_domain,comp);
132  if (max_gpy != zero || min_gpy != zero) {
133  Print() << "Min/max value of dp0/dy are " << min_gpy << " " << max_gpy << std::endl;
134  IntVect min_loc = gradp_temp[1].minIndex(comp);
135  IntVect max_loc = gradp_temp[1].maxIndex(comp);
136  if (min_loc[1] != jlo && min_loc[1] != jhi) amrex::Print() << " with min at face " << min_loc;
137  if (max_loc[1] != jlo && max_loc[1] != jhi) amrex::Print() << " with max at face " << max_loc;
138  Print() << std::endl;
139  } else {
140  Print() << "Min/max value of dp0/dy are zero " << std::endl;
141  }
142 
143  if (solverChoice.terrain_type != TerrainType::EB) {
144  for (MFIter mfi(gradp_temp[2]); mfi.isValid(); ++mfi) {
145  Box bx = mfi.validbox(); bx.growHi(2,-1);
146  if (bx.smallEnd(2) == 0) bx.growLo(2,-1);
147  auto gpz_arr = gradp_temp[2].array(mfi);
148  auto const rhse_arr = r_hse.const_array(mfi);
149  auto const qvhse_arr = qv_hse.const_array(mfi);
150  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
151  gpz_arr(i,j,k) += grav * myhalf * ( rhse_arr(i,j,k ) * (one + qvhse_arr(i,j,k ))
152  +rhse_arr(i,j,k-1) * (one + qvhse_arr(i,j,k-1)) );
153  });
154  }
155  // EB case: check HSE only for uncovered cells
156  } else {
157  for (MFIter mfi(gradp_temp[2]); mfi.isValid(); ++mfi) {
158  Box bx = mfi.validbox(); bx.growHi(2,-1);
159  if (bx.smallEnd(2) == 0) bx.growLo(2,-1);
160  auto gpz_arr = gradp_temp[2].array(mfi);
161  auto const rhse_arr = r_hse.const_array(mfi);
162  auto const qvhse_arr = qv_hse.const_array(mfi);
163  Array4<const Real> w_volfrac = (get_eb(lev).get_w_const_factory())->getVolFrac().const_array(mfi);
164 
165  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
166  if (w_volfrac(i,j,k) > zero) {
167  gpz_arr(i,j,k) += grav * myhalf * ( rhse_arr(i,j,k ) * (one + qvhse_arr(i,j,k ))
168  +rhse_arr(i,j,k-1) * (one + qvhse_arr(i,j,k-1)) );
169  }
170  });
171  }
172  }
173 
174  Real tol;
175 #ifdef AMREX_USE_FLOAT
176  tol = 1.e-4;
177 #else
178  if (solverChoice.terrain_type == TerrainType::EB) {
179  tol = 1.e-4;
180  } else {
181  tol = 1.e-8;
182  }
183 #endif
184 
185  Real min_gpz = gradp_temp[2].min(zface_domain,comp);
186  Real max_gpz = gradp_temp[2].max(zface_domain,comp);
187 
188  if (std::abs(max_gpz) > tol || std::abs(min_gpz) > tol) {
189  IntVect min_loc = gradp_temp[2].minIndex(comp);
190  IntVect max_loc = gradp_temp[2].maxIndex(comp);
191  Print() << "Min/max value of dp0/dz + rho0*|g| are " << min_gpz << " " << max_gpz;
192  if (min_loc[2] != klo && min_loc[2] != khi) amrex::Print() << " with min at face " << min_loc;
193  if (max_loc[2] != klo && max_loc[2] != khi) amrex::Print() << " with max at face " << max_loc;
194  amrex::Abort("Base state is too far out of HSE");
195  } else {
196  Print() << "Min/max value of dp0/dz + rho0*|g| are less than " << tol << std::endl;
197  }
198  Print() << " " << std::endl;
199 
200  if (!solverChoice.anelastic[lev]) {
201 
202  // *******************************************************************************
203  // Now compute for full (moist) pressure
204  // *******************************************************************************
205 
206  MultiFab p(p_hse.boxArray(), p_hse.DistributionMap(), 1, ng);
207  MultiFab rho(lev_new[Vars::cons], make_alias, Rho_comp , 1);
208 
209  if (solverChoice.moisture_type != MoistureType::None) {
210 
211  for (MFIter mfi(rho); mfi.isValid(); ++mfi)
212  {
213  Box gbx = mfi.tilebox();
214  gbx.grow(IntVect(ng,ng,ng));
215  if (gbx.smallEnd(2) < 0) gbx.setSmall(2,0);
216 
217  const Array4<const Real>& cell_data = lev_new[Vars::cons].array(mfi);
218  const Array4<const Real>& r_arr = rho.array(mfi);
219  const Array4< Real>& pp_arr = p.array(mfi);
220  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
221  {
222  Real qv_for_p = cell_data(i,j,k,RhoQ1_comp)/r_arr(i,j,k);
223  pp_arr(i,j,k) = getPgivenRTh(cell_data(i,j,k,RhoTheta_comp),qv_for_p);
224  });
225  }
226  compute_gradp(p, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
227  get_eb(lev), gradp_temp, solverChoice);
228 
229  min_gpx = gradp_temp[0].min(xface_domain,comp);
230  max_gpx = gradp_temp[0].max(xface_domain,comp);
231  if (max_gpx != zero || min_gpx != zero) {
232  Print() << "Min/Max value of x-gradient of full (moist) pressure are " << min_gpx << " " << max_gpx;
233  IntVect min_loc = gradp_temp[0].minIndex(comp);
234  IntVect max_loc = gradp_temp[0].maxIndex(comp);
235  if (min_loc[0] != ilo && min_loc[0] != ihi) amrex::Print() << " with min at face " << min_loc;
236  if (max_loc[0] != ilo && max_loc[0] != ihi) amrex::Print() << " with max at face " << max_loc;
237  Print() << std::endl;
238  } else {
239  Print() << "Min/max value of x-gradient of full (moist) pressure are zero " << std::endl;
240  }
241 
242  min_gpy = gradp_temp[1].min(yface_domain,comp);
243  max_gpy = gradp_temp[1].max(yface_domain,comp);
244  if (max_gpy != zero || min_gpy != zero) {
245  Print() << "Min/Max value of y-gradient of full (moist) pressure are " << min_gpy << " " << max_gpy;
246  IntVect min_loc = gradp_temp[1].minIndex(comp);
247  IntVect max_loc = gradp_temp[1].maxIndex(comp);
248  if (min_loc[1] != jlo && min_loc[1] != jhi) amrex::Print() << " with min at face " << min_loc;
249  if (max_loc[1] != jlo && max_loc[1] != jhi) amrex::Print() << " with max at face " << max_loc;
250  Print() << std::endl;
251  } else {
252  Print() << "Min/max value of y-gradient of full (moist) pressure are zero " << std::endl;
253  }
254 
255  MultiFab qt(rho.boxArray(), rho.DistributionMap(), 1, 1);
256  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
257  make_qt(lev_new[Vars::cons], qt, n_qstate_into_total);
258 
259  for (MFIter mfi(gradp_temp[2]); mfi.isValid(); ++mfi)
260  {
261  Box bx = mfi.validbox(); bx.growHi(2,-1);
262  if (bx.smallEnd(2) == 0) bx.growLo(2,-1);
263  auto gpz_arr = gradp_temp[2].array(mfi);
264  auto const r_arr = rho.const_array(mfi);
265  auto const qt_arr = qt.const_array(mfi);
266 
267  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
268  gpz_arr(i,j,k) += grav * myhalf * (r_arr(i,j,k )*(one+qt_arr(i,j,k )) +
269  r_arr(i,j,k-1)*(one+qt_arr(i,j,k-1)) );
270  });
271  }
272 
273  min_gpz = gradp_temp[2].min(zface_domain,comp);
274  max_gpz = gradp_temp[2].max(zface_domain,comp);
275  if (max_gpz != zero || min_gpz != zero) {
276  IntVect min_loc = gradp_temp[2].minIndex(comp);
277  IntVect max_loc = gradp_temp[2].maxIndex(comp);
278  Print() << "Min/max value of moist dp/dz + rho_m*|g| are " << min_gpz << " " << max_gpz;
279  if (min_loc[2] != klo && min_loc[2] != khi) amrex::Print() << " with min at face " << min_loc;
280  if (max_loc[2] != klo && max_loc[2] != khi) amrex::Print() << " with max at face " << max_loc;
281  Print() << std::endl;
282  } else {
283  Print() << "Min/max value of moist dp/dz + rho_m*|g| are zero " << std::endl;
284  }
285  Print() << " " << std::endl;
286  } // if moist
287  } // if !anelastic
288 }
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
void compute_gradp(const MultiFab &p, const Geometry &geom, const MultiFab &z_phys_nd, const MultiFab &z_phys_cc, Vector< std::unique_ptr< MultiFab >> &mapfac, const eb_ &ebfact, Vector< MultiFab > &gradp, const SolverChoice &solverChoice)
Definition: ERF_MakeGradP.cpp:113
void make_qt(const MultiFab &cons_state, MultiFab &qt, int n_qstate_into_total)
Definition: ERF_Utils.cpp:37
eb_aux_ const * get_w_const_factory() const noexcept
Definition: ERF_EB.H:52
@ qv0_comp
Definition: ERF_IndexDefines.H:77
amrex::Real gravity
Definition: ERF_DataStruct.H:1299
static bool use_real_bcs
Definition: ERF_DataStruct.H:1208
Here is the call graph for this function:

◆ ComputeAndWriteEnsemblePerturbations()

void ERF::ComputeAndWriteEnsemblePerturbations ( )

◆ ComputeDt()

void ERF::ComputeDt ( int  step = -1,
double  cur_time_d = 0.0 
)
private

Function that calls estTimeStep for each level

12 {
13  Vector<Real> dt_tmp(finest_level+1);
14 
15  for (int lev = 0; lev <= finest_level; ++lev)
16  {
17  dt_tmp[lev] = estTimeStep(lev, dt_mri_ratio[lev]);
18  }
19 
20  ParallelDescriptor::ReduceRealMin(&dt_tmp[0], dt_tmp.size());
21 
22  Real dt_0 = dt_tmp[0];
23  int n_factor = 1;
24  for (int lev = 0; lev <= finest_level; ++lev) {
25  dt_tmp[lev] = amrex::min(dt_tmp[lev], change_max*dt[lev]);
26  n_factor *= nsubsteps[lev];
27  dt_0 = amrex::min(dt_0, n_factor*dt_tmp[lev]);
28 
29  }
30  // Limit level 0 time step if requested
31  if (step == 0) {
32  dt_0 *= init_shrink;
33  if (verbose && init_shrink != one) {
34  Print() << "Timestep 0: shrink level 0 initial dt by " << init_shrink << std::endl;
35  }
36  }
37  //
38  // Limit dt by the value of stop_time.
39  // Recall that stop_time is total time, but t_new is elapsed time,
40  // so we must add start_time to t_new
41  //
42  const Real eps = Real(1.e-3)*dt_0;
43  if (cur_time_d + static_cast<double>(dt_0) > static_cast<double>(stop_time - start_time) - static_cast<double>(eps)) {
44  dt_0 = static_cast<Real>(static_cast<double>(stop_time - start_time) - cur_time_d);
45  }
46 
47  dt[0] = dt_0;
48  for (int lev = 1; lev <= finest_level; ++lev) {
49  dt[lev] = dt[lev-1] / nsubsteps[lev];
50  }
51 }
amrex::Real estTimeStep(int lev, long &dt_fast_ratio) const
Definition: ERF_ComputeTimestep.cpp:60
static amrex::Real stop_time
Definition: ERF.H:1140
amrex::Vector< int > nsubsteps
Definition: ERF.H:893
static amrex::Real init_shrink
Definition: ERF.H:1151
static amrex::Real change_max
Definition: ERF.H:1152

Referenced by EvolveOneStep().

Here is the caller graph for this function:

◆ ComputeGhostCells()

static AMREX_FORCE_INLINE int ERF::ComputeGhostCells ( const SolverChoice sc)
inlinestaticprivate
1461  {
1462  int ngrow = 0;
1463 
1464  if (sc.use_num_diff)
1465  {
1466  ngrow = 3;
1467  } else {
1468  if (
1475  { ngrow = 3; }
1476  else if (
1483  { ngrow = 3; }
1484  else if (
1493  { ngrow = 3; }
1494  else if (
1503  { ngrow = 4; }
1504  else
1505  {
1506  if (sc.terrain_type == TerrainType::EB){
1507  ngrow = 4;
1508  } else {
1509  ngrow = 2;
1510  }
1511  }
1512  }
1513 
1514  return ngrow;
1515  }
@ Centered_6th
AdvType moistscal_horiz_adv_type
Definition: ERF_AdvStruct.H:423
AdvType dycore_vert_adv_type
Definition: ERF_AdvStruct.H:420
AdvType moistscal_vert_adv_type
Definition: ERF_AdvStruct.H:424
AdvType dryscal_horiz_adv_type
Definition: ERF_AdvStruct.H:421
AdvType dycore_horiz_adv_type
Definition: ERF_AdvStruct.H:419
AdvType dryscal_vert_adv_type
Definition: ERF_AdvStruct.H:422
AdvChoice advChoice
Definition: ERF_DataStruct.H:1219

◆ ComputeGlobalMinLocation_WRF()

void ERF::ComputeGlobalMinLocation_WRF ( const amrex::Geometry &  geom,
const amrex::Vector< amrex::MultiFab > &  S_data,
amrex::Real d_val_min_ptr,
int *  d_i_min_ptr,
int *  d_j_min_ptr,
amrex::Real global_val_min,
int &  global_i_min,
int &  global_j_min 
)
44 {
45  Real h_val_min;
46  int h_i_min, h_j_min;
47 
48  Gpu::copy(Gpu::deviceToHost, d_val_min_ptr, d_val_min_ptr + 1, &h_val_min);
49  Gpu::copy(Gpu::deviceToHost, d_i_min_ptr, d_i_min_ptr + 1, &h_i_min);
50  Gpu::copy(Gpu::deviceToHost, d_j_min_ptr, d_j_min_ptr + 1, &h_j_min);
51  Gpu::synchronize();
52 
53  Real local_val_min = h_val_min;
54  int local_i_min = h_i_min;
55  int local_j_min = h_j_min;
56 
57  int rank = ParallelDescriptor::MyProc();
58 
59  in.value = local_val_min;
60  in.rank = rank;
61 
62  #ifdef AMREX_USE_MPI
63  MPI_Allreduce(&in, &out, 1, MPI_DOUBLE_INT, MPI_MINLOC, MPI_COMM_WORLD);
64  #else
65  out = in;
66  #endif
67 
68  global_val_min = out.value;
69  int owner_rank = out.rank;
70 
71  // Broadcast the indices from the rank that owns the minimum
72  global_i_min = local_i_min;
73  global_j_min = local_j_min;
74 
75  ParallelDescriptor::Bcast(&global_i_min, 1, owner_rank);
76  ParallelDescriptor::Bcast(&global_j_min, 1, owner_rank);
77 
78  if (rank == 0) {
79  Print() << "Global minimum distance to hurricane eye (k=0): "
80  << global_val_min << " at (i,j) = ("
81  << global_i_min << ", " << global_j_min << ")\n";
82  }
83 
84  Gpu::DeviceScalar<Real> d_eye_lat(zero), d_eye_lon(zero);
85 
86  Real* d_eye_lat_ptr = d_eye_lat.dataPtr();
87  Real* d_eye_lon_ptr = d_eye_lon.dataPtr();
88 
89  int levc = finest_level;
90  // On owner_rank, compute eye_lat and eye_lon
91  if (rank == owner_rank) {
92  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
93  const Box& box = mfi.validbox();
94  FArrayBox& fab_lat = (*(lat_m[levc]))[mfi];
95  FArrayBox& fab_lon = (*(lon_m[levc]))[mfi];
96  const Array4<Real>& lat_arr = fab_lat.array();
97  const Array4<Real>& lon_arr = fab_lon.array();
98 
99  if (box.smallEnd()[2] == 0) {
100  Box bx2d = makeSlab(box,2,0);
101  ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int ) {
102  if (i == global_i_min && j == global_j_min) {
103  *d_eye_lat_ptr = lat_arr(i,j,0);
104  *d_eye_lon_ptr = lon_arr(i,j,0);
105  }
106  });
107  }
108  }
109  }
110 
111  Real eye_lat = d_eye_lat.dataValue();
112  Real eye_lon = d_eye_lon.dataValue();
113 
114  // Synchronize to ensure the owner has computed values
115  Gpu::synchronize();
116 
117  ParallelDescriptor::Bcast(&eye_lat, 1, owner_rank);
118  ParallelDescriptor::Bcast(&eye_lon, 1, owner_rank);
119 
120  const auto dx = lev_geom.CellSizeArray();
121  const auto prob_lo = lev_geom.ProbLoArray();
122 
123  Real eye_x = prob_lo[0] + (global_i_min+myhalf)*dx[0];
124  Real eye_y = prob_lo[1] + (global_j_min+myhalf)*dx[1];
125 
126  hurricane_eye_track_xy.push_back({eye_x, eye_y});
127  hurricane_eye_track_latlon.push_back({eye_lon, eye_lat});
128 }
struct @29 out
int rank
Definition: ERF_HurricaneDiagnostics_WRF.cpp:31
struct @29 in
const amrex::Real * prob_lo
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:16
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_eye_track_xy
Definition: ERF.H:169
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_eye_track_latlon
Definition: ERF.H:170
Here is the call graph for this function:

◆ Construct_ERFFillPatchers()

void ERF::Construct_ERFFillPatchers ( int  lev)
private
2718 {
2719  auto& fine_new = vars_new[lev];
2720  auto& crse_new = vars_new[lev-1];
2721  auto& ba_fine = fine_new[Vars::cons].boxArray();
2722  auto& ba_crse = crse_new[Vars::cons].boxArray();
2723  auto& dm_fine = fine_new[Vars::cons].DistributionMap();
2724  auto& dm_crse = crse_new[Vars::cons].DistributionMap();
2725 
2726  int ncomp = vars_new[lev][Vars::cons].nComp();
2727 
2728  FPr_c.emplace_back(ba_fine, dm_fine, geom[lev] ,
2729  ba_crse, dm_crse, geom[lev-1],
2730  -cf_width, -cf_set_width, ncomp, &cell_cons_interp);
2731  FPr_u.emplace_back(convert(ba_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
2732  convert(ba_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
2733  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
2734  FPr_v.emplace_back(convert(ba_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
2735  convert(ba_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
2736  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
2737  FPr_w.emplace_back(convert(ba_fine, IntVect(0,0,1)), dm_fine, geom[lev] ,
2738  convert(ba_crse, IntVect(0,0,1)), dm_crse, geom[lev-1],
2739  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
2740 }
int cf_set_width
Definition: ERF.H:991

◆ create_background_state_for_ensemble()

void ERF::create_background_state_for_ensemble ( int  lev,
amrex::MultiFab &  mf_cc_pert,
amrex::MultiFab &  cons_pert,
amrex::MultiFab &  xvel_pert,
amrex::MultiFab &  yvel_pert,
amrex::MultiFab &  zvel_pert 
)
521 {
522 
523  ignore_unused(lev);
524  int nx_crse, ny_crse, nz_crse, ng_crse, ncomp_crse;
525  Vector<Vector<Real>> data_crse;
526  std::array<Real,3> problo_ext, probhi_ext;
527 
528  Vector<Real> data_rho, data_theta, data_xvel, data_yvel, data_zvel;
529 
531  nx_crse, ny_crse, nz_crse, ng_crse, ncomp_crse,
532  problo_ext, probhi_ext,
533  data_rho, data_theta, data_xvel, data_yvel, data_zvel);
534 
535  Geometry& geom_fine = geom[0];
536  // Create a cell-centered multifab on the fine mesh - ie. something with the same boxarray,
537  // distributed mapping, nGrow, but with 5 components
538  MultiFab mf_cc_fine;
539  const MultiFab& src = vars_new[0][0];
540  int ncomp = 5;
541  mf_cc_fine.define(src.boxArray(), src.DistributionMap(),
542  ncomp, src.nGrow());
543 
544  InterpolateToFineMF(data_rho, data_theta, data_xvel, data_yvel, data_zvel,
545  nx_crse, ny_crse, nz_crse,
546  problo_ext, probhi_ext,
547  mf_cc_fine,
548  geom_fine);
549 
550  ApplyNeumannBCs(geom_fine, mf_cc_fine);
551 
552  Vector<std::string> varnames = {"density","theta", "x_velocity","y_velocity","z_velocity"};
553 
554  // Add pertubrations stored in the "pert" variables in the function arguments
555  // (multiplied by the corresponding amplitude)
556  AddPertToBckgnd(mf_cc_fine, mf_cc_pert);
557  ApplyNeumannBCs(geom_fine, mf_cc_fine);
558  //WriteSingleLevelPlotfile("1_plt_final", mf_cc_fine, varnames, geom_fine, zero, 0);
559 
560  MakeFinalMultiFabs(mf_cc_fine, cons_pert, xvel_pert, yvel_pert, zvel_pert);
561 }
void InterpolateToFineMF(const Vector< Real > &data_rho, const Vector< Real > &data_theta, const Vector< Real > &data_xvel, const Vector< Real > &data_yvel, const Vector< Real > &data_zvel, int nx, int ny, int nz, const std::array< Real, 3 > &problo, const std::array< Real, 3 > &probhi, MultiFab &mf_fine, const Geometry &geom_fine)
Definition: ERF_InitForEnsemble.cpp:325
void ReadCustomDataFile(const std::string &filename_custom, int &nx, int &ny, int &nz, int &ng, int &ncomp, std::array< Real, 3 > &problo_ext, std::array< Real, 3 > &probhi_ext, Vector< Real > &data_rho, Vector< Real > &data_theta, Vector< Real > &data_xvel, Vector< Real > &data_yvel, Vector< Real > &data_zvel)
Definition: ERF_InitForEnsemble.cpp:208
void ApplyNeumannBCs(const Geometry &geom, MultiFab &mf_cc)
Definition: ERF_InitForEnsemble.cpp:162
void AddPertToBckgnd(MultiFab &mf_cc_fine, const MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:490
void MakeFinalMultiFabs(const MultiFab &mf_cc_fine, MultiFab &cons_pert, MultiFab &xvel_pert, MultiFab &yvel_pert, MultiFab &zvel_pert)
Definition: ERF_InitForEnsemble.cpp:426
std::string coarse_bckgnd_data_file
Definition: ERF_DataStruct.H:1450
Here is the call graph for this function:

◆ create_random_perturbations()

void ERF::create_random_perturbations ( const int  lev,
amrex::MultiFab &  mf_cc_pert 
)
16 {
17  const MultiFab& src = vars_new[lev][Vars::cons];
18 
19  int ncomp = 5;
20  mf_cc_pert.define(src.boxArray(), src.DistributionMap(),
21  ncomp, src.nGrow());
22 
23  // Loop over cell-centered boxes
24  for (MFIter mfi(mf_cc_pert, TilingIfNotGPU()); mfi.isValid(); ++mfi)
25  {
26  const Box& bx = mfi.tilebox();
27 
28  auto const& pert_arr = mf_cc_pert.array(mfi);
29 
30  // Loop over all 5 components
31  amrex::ParallelForRNG(bx, ncomp,
32  [=] AMREX_GPU_DEVICE (int i, int j, int k, int n,
33  const amrex::RandomEngine& engine) noexcept
34  {
35  pert_arr(i,j,k,n) = amrex::Random(engine);
36  });
37  }
38 }
ParallelForRNG(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k, const amrex::RandomEngine &engine) noexcept { const Real x=prob_lo_x+(i+myhalf) *dx;const Real y=prob_lo_y+(j+myhalf) *dy;const Real z=z_cc(i, j, k);const Real r=std::sqrt((x-xc) *(x-xc)+(y-yc) *(y-yc)+(z-zc) *(z-zc));if((z<=pert_ref_height) &&(T_0_Pert_Mag !=amrex::Real(0))) { Real rand_double=amrex::Random(engine);state_pert(i, j, k, RhoTheta_comp)=(rand_double *amrex::Real(2) - amrex::Real(1)) *T_0_Pert_Mag;if(!pert_rhotheta) { state_pert(i, j, k, RhoTheta_comp) *=r_hse(i, j, k);} } state_pert(i, j, k, RhoScalar_comp)=A_0 *std::exp(-amrex::Real(10.) *r *r);if(state_pert.nComp() > RhoKE_comp) { if(rhoKE_0 > 0) { state_pert(i, j, k, RhoKE_comp)=rhoKE_0;} else { state_pert(i, j, k, RhoKE_comp)=r_hse(i, j, k) *KE_0;} if(KE_decay_height > 0) { state_pert(i, j, k, RhoKE_comp) *=amrex::max(std::pow(1 - amrex::min(z/KE_decay_height, amrex::Real(1)), KE_decay_order), Real(1e-12));} } })
Here is the call graph for this function:

◆ DataLog()

AMREX_FORCE_INLINE std::ostream& ERF::DataLog ( int  i)
inlineprivate
1526  {
1527  return *datalog[i];
1528  }
amrex::Vector< std::unique_ptr< std::fstream > > datalog
Definition: ERF.H:1705

◆ DataLogName()

std::string ERF::DataLogName ( int  i) const
inlineprivatenoexcept

The filename of the ith datalog file.

1721 { return datalogname[i]; }
amrex::Vector< std::string > datalogname
Definition: ERF.H:1708

◆ Define_ERFFillPatchers()

void ERF::Define_ERFFillPatchers ( int  lev)
private
2744 {
2745  auto& fine_new = vars_new[lev];
2746  auto& crse_new = vars_new[lev-1];
2747  auto& ba_fine = fine_new[Vars::cons].boxArray();
2748  auto& ba_crse = crse_new[Vars::cons].boxArray();
2749  auto& dm_fine = fine_new[Vars::cons].DistributionMap();
2750  auto& dm_crse = crse_new[Vars::cons].DistributionMap();
2751 
2752  int ncomp = fine_new[Vars::cons].nComp();
2753 
2754  FPr_c[lev-1].Define(ba_fine, dm_fine, geom[lev] ,
2755  ba_crse, dm_crse, geom[lev-1],
2756  -cf_width, -cf_set_width, ncomp, &cell_cons_interp);
2757  FPr_u[lev-1].Define(convert(ba_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
2758  convert(ba_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
2759  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
2760  FPr_v[lev-1].Define(convert(ba_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
2761  convert(ba_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
2762  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
2763  FPr_w[lev-1].Define(convert(ba_fine, IntVect(0,0,1)), dm_fine, geom[lev] ,
2764  convert(ba_crse, IntVect(0,0,1)), dm_crse, geom[lev-1],
2765  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
2766 }

◆ DerDataLog()

AMREX_FORCE_INLINE std::ostream& ERF::DerDataLog ( int  i)
inlineprivate
1533  {
1534  return *der_datalog[i];
1535  }
amrex::Vector< std::unique_ptr< std::fstream > > der_datalog
Definition: ERF.H:1706

◆ DerDataLogName()

std::string ERF::DerDataLogName ( int  i) const
inlineprivatenoexcept
1722 { return der_datalogname[i]; }
amrex::Vector< std::string > der_datalogname
Definition: ERF.H:1709

◆ derive_diag_profiles()

void ERF::derive_diag_profiles ( double  time,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_u,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_v,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_w,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_rho,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_th,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_ksgs,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_Kmv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_Khv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qc,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qr,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wqv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wqc,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wqr,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qi,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qs,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qg,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uu,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_ww,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_thth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_ku,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_kv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_kw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_p,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_pu,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_pv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_pw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wthv 
)

Computes the profiles for diagnostic quantities.

Parameters
h_avg_uProfile for x-velocity on Host
h_avg_vProfile for y-velocity on Host
h_avg_wProfile for z-velocity on Host
h_avg_rhoProfile for density on Host
h_avg_thProfile for potential temperature on Host
h_avg_ksgsProfile for Kinetic Energy on Host
h_avg_uuProfile for x-velocity squared on Host
h_avg_uvProfile for x-velocity * y-velocity on Host
h_avg_uwProfile for x-velocity * z-velocity on Host
h_avg_vvProfile for y-velocity squared on Host
h_avg_vwProfile for y-velocity * z-velocity on Host
h_avg_wwProfile for z-velocity squared on Host
h_avg_uthProfile for x-velocity * potential temperature on Host
h_avg_uiuiuProfile for u_i*u_i*u triple product on Host
h_avg_uiuivProfile for u_i*u_i*v triple product on Host
h_avg_uiuiwProfile for u_i*u_i*w triple product on Host
h_avg_pProfile for pressure perturbation on Host
h_avg_puProfile for pressure perturbation * x-velocity on Host
h_avg_pvProfile for pressure perturbation * y-velocity on Host
h_avg_pwProfile for pressure perturbation * z-velocity on Host
205 {
206  // We assume that this is always called at level 0
207  int lev = 0;
208 
209  bool l_use_kturb = solverChoice.turbChoice[lev].use_kturb;
210  bool l_use_KE = solverChoice.turbChoice[lev].use_tke;
211  // This will hold rho, theta, ksgs, Kmh, Kmv, uu, uv, uw, vv, vw, ww, uth, vth, wth,
212  // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
213  // thth, uiuiu, uiuiv, uiuiw, p, pu, pv, pw, qv, qc, qr, wqv, wqc, wqr,
214  // 14 15 16 17 18 19 20 21 22 23 24 25 26 27
215  // qi, qs, qg, wthv
216  // 28 29 30 31
217  MultiFab mf_out(grids[lev], dmap[lev], 32, 0);
218 
219  MultiFab mf_vels(grids[lev], dmap[lev], AMREX_SPACEDIM, 0);
220 
221  MultiFab u_cc(mf_vels, make_alias, 0, 1); // u at cell centers
222  MultiFab v_cc(mf_vels, make_alias, 1, 1); // v at cell centers
223  MultiFab w_cc(mf_vels, make_alias, 2, 1); // w at cell centers
224 
225  average_face_to_cellcenter(mf_vels,0,
226  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],&vars_new[lev][Vars::yvel],&vars_new[lev][Vars::zvel]});
227 
228  int zdir = 2;
229  auto domain = geom[0].Domain();
230 
231  // Sum in the horizontal plane
232  h_avg_u = sumToLine(mf_vels ,0,1,domain,zdir);
233  h_avg_v = sumToLine(mf_vels ,1,1,domain,zdir);
234  h_avg_w = sumToLine(mf_vels ,2,1,domain,zdir);
235 
236  int hu_size = h_avg_u.size();
237 
238  // Divide by the total number of cells we are averaging over
239  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
240  for (int k = 0; k < hu_size; ++k) {
241  h_avg_u[k] /= area_z; h_avg_v[k] /= area_z; h_avg_w[k] /= area_z;
242  }
243 
244  Gpu::DeviceVector<Real> d_avg_u(hu_size, zero);
245  Gpu::DeviceVector<Real> d_avg_v(hu_size, zero);
246  Gpu::DeviceVector<Real> d_avg_w(hu_size, zero);
247 
248 #if 0
249  auto* avg_u_ptr = d_avg_u.data();
250  auto* avg_v_ptr = d_avg_v.data();
251  auto* avg_w_ptr = d_avg_w.data();
252 #endif
253 
254  Gpu::copy(Gpu::hostToDevice, h_avg_u.begin(), h_avg_u.end(), d_avg_u.begin());
255  Gpu::copy(Gpu::hostToDevice, h_avg_v.begin(), h_avg_v.end(), d_avg_v.begin());
256  Gpu::copy(Gpu::hostToDevice, h_avg_w.begin(), h_avg_w.end(), d_avg_w.begin());
257 
258  int nvars = vars_new[lev][Vars::cons].nComp();
259  MultiFab mf_cons(vars_new[lev][Vars::cons], make_alias, 0, nvars);
260 
261  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
262 
263  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
264  const MultiFab* eta_src = nullptr;
265  const bool have_native_shoc_diagnostics =
266 #ifdef ERF_USE_NATIVE_SHOC
267  solverChoice.turbChoice[lev].uses_native_shoc() &&
268  native_shoc_driver[lev] &&
269  native_shoc_driver[lev]->has_native_diagnostics();
270 #else
271  false;
272 #endif
273  if (l_use_kturb) {
274 #ifdef ERF_USE_NATIVE_SHOC
275  if (have_native_shoc_diagnostics) {
276  eta_src = &native_shoc_driver[lev]->native_diagnostics();
277  } else
278 #endif
279  {
280  eta_src = eddyDiffs_lev[lev].get();
281  }
282  }
283 
284  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
285  {
286  const Box& bx = mfi.tilebox();
287  const Array4<Real>& fab_arr = mf_out.array(mfi);
288  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
289  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
290  const Array4<Real>& w_cc_arr = w_cc.array(mfi);
291  const Array4<Real>& cons_arr = mf_cons.array(mfi);
292  const Array4<Real>& p0_arr = p_hse.array(mfi);
293  const Array4<const Real>& eta_arr = (eta_src) ? eta_src->const_array(mfi) :
294  Array4<const Real>{};
295 
296  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
297  {
298  Real theta = cons_arr(i,j,k,RhoTheta_comp) / cons_arr(i,j,k,Rho_comp);
299  fab_arr(i, j, k, 0) = cons_arr(i,j,k,Rho_comp);
300  fab_arr(i, j, k, 1) = theta;
301  Real ksgs = zero;
302  if (l_use_KE) {
303  ksgs = cons_arr(i,j,k,RhoKE_comp) / cons_arr(i,j,k,Rho_comp);
304  }
305  fab_arr(i, j, k, 2) = ksgs;
306 #if 1
307  if (l_use_kturb) {
308  fab_arr(i, j, k, 3) = eta_arr(i,j,k,EddyDiff::Mom_v); // Kmv
309  fab_arr(i, j, k, 4) = eta_arr(i,j,k,EddyDiff::Theta_v); // Khv
310  } else {
311  fab_arr(i, j, k, 3) = zero;
312  fab_arr(i, j, k, 4) = zero;
313  }
314 #else
315  // Here we hijack the "Kturb" variable name to print out the resolved kinetic energy
316  Real upert = u_cc_arr(i,j,k) - avg_u_ptr[k];
317  Real vpert = v_cc_arr(i,j,k) - avg_v_ptr[k];
318  Real wpert = w_cc_arr(i,j,k) - avg_w_ptr[k];
319  fab_arr(i, j, k, 3) = myhalf * (upert*upert + vpert*vpert + wpert*wpert);
320 #endif
321  fab_arr(i, j, k, 5) = u_cc_arr(i,j,k) * u_cc_arr(i,j,k); // u*u
322  fab_arr(i, j, k, 6) = u_cc_arr(i,j,k) * v_cc_arr(i,j,k); // u*v
323  fab_arr(i, j, k, 7) = u_cc_arr(i,j,k) * w_cc_arr(i,j,k); // u*w
324  fab_arr(i, j, k, 8) = v_cc_arr(i,j,k) * v_cc_arr(i,j,k); // v*v
325  fab_arr(i, j, k, 9) = v_cc_arr(i,j,k) * w_cc_arr(i,j,k); // v*w
326  fab_arr(i, j, k,10) = w_cc_arr(i,j,k) * w_cc_arr(i,j,k); // w*w
327  fab_arr(i, j, k,11) = u_cc_arr(i,j,k) * theta; // u*th
328  fab_arr(i, j, k,12) = v_cc_arr(i,j,k) * theta; // v*th
329  fab_arr(i, j, k,13) = w_cc_arr(i,j,k) * theta; // w*th
330  fab_arr(i, j, k,14) = theta * theta; // th*th
331 
332  // if the number of fields is changed above, then be sure to update
333  // the following def!
334  Real uiui = fab_arr(i,j,k,5) + fab_arr(i,j,k,8) + fab_arr(i,j,k,10);
335  fab_arr(i, j, k,15) = uiui * u_cc_arr(i,j,k); // (ui*ui)*u
336  fab_arr(i, j, k,16) = uiui * v_cc_arr(i,j,k); // (ui*ui)*v
337  fab_arr(i, j, k,17) = uiui * w_cc_arr(i,j,k); // (ui*ui)*w
338 
339  if (!use_moisture) {
340  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp));
341  p -= p0_arr(i,j,k);
342  fab_arr(i, j, k,18) = p; // p
343  fab_arr(i, j, k,19) = p * u_cc_arr(i,j,k); // p*u
344  fab_arr(i, j, k,20) = p * v_cc_arr(i,j,k); // p*v
345  fab_arr(i, j, k,21) = p * w_cc_arr(i,j,k); // p*w
346  fab_arr(i, j, k,22) = zero; // qv
347  fab_arr(i, j, k,23) = zero; // qc
348  fab_arr(i, j, k,24) = zero; // qr
349  fab_arr(i, j, k,25) = zero; // w*qv
350  fab_arr(i, j, k,26) = zero; // w*qc
351  fab_arr(i, j, k,27) = zero; // w*qr
352  fab_arr(i, j, k,28) = zero; // qi
353  fab_arr(i, j, k,29) = zero; // qs
354  fab_arr(i, j, k,30) = zero; // qg
355  fab_arr(i, j, k,31) = zero; // w*thv
356  }
357  });
358  } // mfi
359 
360  if (use_moisture)
361  {
362  int n_qstate_moist = micro->Get_Qstate_Moist_Size();
363 
364  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
365  {
366  const Box& bx = mfi.tilebox();
367  const Array4<Real>& fab_arr = mf_out.array(mfi);
368  const Array4<Real>& cons_arr = mf_cons.array(mfi);
369  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
370  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
371  const Array4<Real>& w_cc_arr = w_cc.array(mfi);
372  const Array4<Real>& p0_arr = p_hse.array(mfi);
373 
374  int rhoqr_comp = solverChoice.moisture_indices.qr;
375 
376  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
377  {
378  Real qv = cons_arr(i,j,k,RhoQ1_comp) / cons_arr(i,j,k,Rho_comp);
379  Real qc = cons_arr(i,j,k,RhoQ2_comp) / cons_arr(i,j,k,Rho_comp);
380  Real qr = (rhoqr_comp > -1) ? cons_arr(i,j,k,rhoqr_comp) / cons_arr(i,j,k,Rho_comp) :
381  zero;
382  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp), qv);
383 
384  p -= p0_arr(i,j,k);
385  fab_arr(i, j, k,18) = p; // p
386  fab_arr(i, j, k,19) = p * u_cc_arr(i,j,k); // p*u
387  fab_arr(i, j, k,20) = p * v_cc_arr(i,j,k); // p*v
388  fab_arr(i, j, k,21) = p * w_cc_arr(i,j,k); // p*w
389  fab_arr(i, j, k,22) = qv; // qv
390  fab_arr(i, j, k,23) = qc; // qc
391  fab_arr(i, j, k,24) = qr; // qr
392  fab_arr(i, j, k,25) = w_cc_arr(i,j,k) * qv; // w*qv
393  fab_arr(i, j, k,26) = w_cc_arr(i,j,k) * qc; // w*qc
394  fab_arr(i, j, k,27) = w_cc_arr(i,j,k) * qr; // w*qr
395  if (n_qstate_moist > 3) {
396  fab_arr(i, j, k,28) = cons_arr(i,j,k,RhoQ3_comp) / cons_arr(i,j,k,Rho_comp); // qi
397  fab_arr(i, j, k,29) = cons_arr(i,j,k,RhoQ5_comp) / cons_arr(i,j,k,Rho_comp); // qs
398  fab_arr(i, j, k,30) = cons_arr(i,j,k,RhoQ6_comp) / cons_arr(i,j,k,Rho_comp); // qg
399  } else {
400  fab_arr(i, j, k,28) = zero; // qi
401  fab_arr(i, j, k,29) = zero; // qs
402  fab_arr(i, j, k,30) = zero; // qg
403  }
404  Real ql = qc + qr;
405  Real theta = cons_arr(i,j,k,RhoTheta_comp) / cons_arr(i,j,k,Rho_comp);
406  Real thv = theta * (1 + Real(0.61)*qv - ql);
407  fab_arr(i, j, k,31) = w_cc_arr(i,j,k) * thv; // w*thv
408  });
409  } // mfi
410  } // use_moisture
411 
412  h_avg_rho = sumToLine(mf_out, 0,1,domain,zdir);
413  h_avg_th = sumToLine(mf_out, 1,1,domain,zdir);
414  h_avg_ksgs = sumToLine(mf_out, 2,1,domain,zdir);
415  h_avg_Kmv = sumToLine(mf_out, 3,1,domain,zdir);
416  h_avg_Khv = sumToLine(mf_out, 4,1,domain,zdir);
417  h_avg_uu = sumToLine(mf_out, 5,1,domain,zdir);
418  h_avg_uv = sumToLine(mf_out, 6,1,domain,zdir);
419  h_avg_uw = sumToLine(mf_out, 7,1,domain,zdir);
420  h_avg_vv = sumToLine(mf_out, 8,1,domain,zdir);
421  h_avg_vw = sumToLine(mf_out, 9,1,domain,zdir);
422  h_avg_ww = sumToLine(mf_out,10,1,domain,zdir);
423  h_avg_uth = sumToLine(mf_out,11,1,domain,zdir);
424  h_avg_vth = sumToLine(mf_out,12,1,domain,zdir);
425  h_avg_wth = sumToLine(mf_out,13,1,domain,zdir);
426  h_avg_thth = sumToLine(mf_out,14,1,domain,zdir);
427  h_avg_uiuiu = sumToLine(mf_out,15,1,domain,zdir);
428  h_avg_uiuiv = sumToLine(mf_out,16,1,domain,zdir);
429  h_avg_uiuiw = sumToLine(mf_out,17,1,domain,zdir);
430  h_avg_p = sumToLine(mf_out,18,1,domain,zdir);
431  h_avg_pu = sumToLine(mf_out,19,1,domain,zdir);
432  h_avg_pv = sumToLine(mf_out,20,1,domain,zdir);
433  h_avg_pw = sumToLine(mf_out,21,1,domain,zdir);
434  h_avg_qv = sumToLine(mf_out,22,1,domain,zdir);
435  h_avg_qc = sumToLine(mf_out,23,1,domain,zdir);
436  h_avg_qr = sumToLine(mf_out,24,1,domain,zdir);
437  h_avg_wqv = sumToLine(mf_out,25,1,domain,zdir);
438  h_avg_wqc = sumToLine(mf_out,26,1,domain,zdir);
439  h_avg_wqr = sumToLine(mf_out,27,1,domain,zdir);
440  h_avg_qi = sumToLine(mf_out,28,1,domain,zdir);
441  h_avg_qs = sumToLine(mf_out,29,1,domain,zdir);
442  h_avg_qg = sumToLine(mf_out,30,1,domain,zdir);
443  h_avg_wthv = sumToLine(mf_out,31,1,domain,zdir);
444 
445  // Divide by the total number of cells we are averaging over
446  int h_avg_u_size = static_cast<int>(h_avg_u.size());
447  for (int k = 0; k < h_avg_u_size; ++k) {
448  h_avg_rho[k] /= area_z;
449  h_avg_ksgs[k] /= area_z;
450  h_avg_Kmv[k] /= area_z;
451  h_avg_Khv[k] /= area_z;
452  h_avg_th[k] /= area_z;
453  h_avg_thth[k] /= area_z;
454  h_avg_uu[k] /= area_z;
455  h_avg_uv[k] /= area_z;
456  h_avg_uw[k] /= area_z;
457  h_avg_vv[k] /= area_z;
458  h_avg_vw[k] /= area_z;
459  h_avg_ww[k] /= area_z;
460  h_avg_uth[k] /= area_z;
461  h_avg_vth[k] /= area_z;
462  h_avg_wth[k] /= area_z;
463  h_avg_uiuiu[k] /= area_z;
464  h_avg_uiuiv[k] /= area_z;
465  h_avg_uiuiw[k] /= area_z;
466  h_avg_p[k] /= area_z;
467  h_avg_pu[k] /= area_z;
468  h_avg_pv[k] /= area_z;
469  h_avg_pw[k] /= area_z;
470  h_avg_qv[k] /= area_z;
471  h_avg_qc[k] /= area_z;
472  h_avg_qr[k] /= area_z;
473  h_avg_wqv[k] /= area_z;
474  h_avg_wqc[k] /= area_z;
475  h_avg_wqr[k] /= area_z;
476  h_avg_qi[k] /= area_z;
477  h_avg_qs[k] /= area_z;
478  h_avg_qg[k] /= area_z;
479  h_avg_wthv[k] /= area_z;
480  }
481 
482 #if 0
483  // Here we print the integrated total kinetic energy as computed in the 1D profile above
484  Real sum = zero;
485  Real dz = geom[0].ProbHi(2) / static_cast<Real>(h_avg_u_size);
486  for (int k = 0; k < h_avg_u_size; ++k) {
487  sum += h_avg_kturb[k] * h_avg_rho[k] * dz;
488  }
489  amrex::Print() << "ITKE " << time << " " << sum << " using " << h_avg_u_size << " " << dz << std::endl;
490 #endif
491 }
#define RhoQ3_comp
Definition: ERF_IndexDefines.H:44
#define RhoQ6_comp
Definition: ERF_IndexDefines.H:47
#define RhoQ5_comp
Definition: ERF_IndexDefines.H:46
#define RhoKE_comp
Definition: ERF_IndexDefines.H:38
const bool use_moisture
Definition: ERF_InitCustomPert_Bomex.H:14
@ Theta_v
Definition: ERF_IndexDefines.H:210
@ Mom_v
Definition: ERF_IndexDefines.H:209
@ theta
Definition: ERF_MM5.H:20
@ qr
Definition: ERF_AdvanceWSM6.cpp:112
Here is the call graph for this function:

◆ derive_diag_profiles_stag()

void ERF::derive_diag_profiles_stag ( double  time,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_u,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_v,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_w,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_rho,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_th,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_ksgs,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_Kmv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_Khv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qc,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qr,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wqv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wqc,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wqr,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qi,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qs,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qg,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uu,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_ww,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_uth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_thth,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_ku,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_kv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_kw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_p,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_pu,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_pv,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_pw,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wthv 
)

Computes the profiles for diagnostic quantities at staggered heights.

Parameters
h_avg_uProfile for x-velocity on Host
h_avg_vProfile for y-velocity on Host
h_avg_wProfile for z-velocity on Host
h_avg_rhoProfile for density on Host
h_avg_thProfile for potential temperature on Host
h_avg_ksgsProfile for Kinetic Energy on Host
h_avg_uuProfile for x-velocity squared on Host
h_avg_uvProfile for x-velocity * y-velocity on Host
h_avg_uwProfile for x-velocity * z-velocity on Host
h_avg_vvProfile for y-velocity squared on Host
h_avg_vwProfile for y-velocity * z-velocity on Host
h_avg_wwProfile for z-velocity squared on Host
h_avg_uthProfile for x-velocity * potential temperature on Host
h_avg_uiuiuProfile for u_i*u_i*u triple product on Host
h_avg_uiuivProfile for u_i*u_i*v triple product on Host
h_avg_uiuiwProfile for u_i*u_i*w triple product on Host
h_avg_pProfile for pressure perturbation on Host
h_avg_puProfile for pressure perturbation * x-velocity on Host
h_avg_pvProfile for pressure perturbation * y-velocity on Host
h_avg_pwProfile for pressure perturbation * z-velocity on Host
311 {
312  // We assume that this is always called at level 0
313  int lev = 0;
314 
315  bool l_use_kturb = solverChoice.turbChoice[lev].use_kturb;
316  bool l_use_KE = solverChoice.turbChoice[lev].use_tke;
317  // Note: "uiui" == u_i*u_i = u*u + v*v + w*w
318  // This will hold rho, theta, ksgs, Kmh, Kmv, uu, uv, vv, uth, vth,
319  // indices: 0 1 2 3 4 5 6 7 8 9
320  // thth, uiuiu, uiuiv, p, pu, pv, qv, qc, qr, qi, qs, qg
321  // 10 11 12 13 14 15 16 17 18 19 20 21
322  MultiFab mf_out(grids[lev], dmap[lev], 22, 0);
323 
324  // This will hold uw, vw, ww, wth, uiuiw, pw, wqv, wqc, wqr, wthv
325  // indices: 0 1 2 3 4 5 6 7 8 9
326  MultiFab mf_out_stag(convert(grids[lev], IntVect(0,0,1)), dmap[lev], 10, 0);
327 
328  // This is only used to average u and v; w is not averaged to cell centers
329  MultiFab mf_vels(grids[lev], dmap[lev], 2, 0);
330 
331  MultiFab u_cc(mf_vels, make_alias, 0, 1); // u at cell centers
332  MultiFab v_cc(mf_vels, make_alias, 1, 1); // v at cell centers
333  MultiFab w_fc(vars_new[lev][Vars::zvel], make_alias, 0, 1); // w at face centers (staggered)
334 
335  int zdir = 2;
336  auto domain = geom[0].Domain();
337  Box stag_domain = domain;
338  stag_domain.convert(IntVect(0,0,1));
339 
340  int nvars = vars_new[lev][Vars::cons].nComp();
341  MultiFab mf_cons(vars_new[lev][Vars::cons], make_alias, 0, nvars);
342 
343  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
344 
345  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
346  const MultiFab* eta_src = nullptr;
347  const bool have_native_shoc_diagnostics =
348 #ifdef ERF_USE_NATIVE_SHOC
349  solverChoice.turbChoice[lev].uses_native_shoc() &&
350  native_shoc_driver[lev] &&
351  native_shoc_driver[lev]->has_native_diagnostics();
352 #else
353  false;
354 #endif
355  if (l_use_kturb) {
356 #ifdef ERF_USE_NATIVE_SHOC
357  if (have_native_shoc_diagnostics) {
358  eta_src = &native_shoc_driver[lev]->native_diagnostics();
359  } else
360 #endif
361  {
362  eta_src = eddyDiffs_lev[lev].get();
363  }
364  }
365 
366  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
367  {
368  const Box& bx = mfi.tilebox();
369  const Array4<Real>& fab_arr = mf_out.array(mfi);
370  const Array4<Real>& fab_arr_stag = mf_out_stag.array(mfi);
371  const Array4<Real>& u_arr = vars_new[lev][Vars::xvel].array(mfi);
372  const Array4<Real>& v_arr = vars_new[lev][Vars::yvel].array(mfi);
373  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
374  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
375  const Array4<Real>& w_fc_arr = w_fc.array(mfi);
376  const Array4<Real>& cons_arr = mf_cons.array(mfi);
377  const Array4<Real>& p0_arr = p_hse.array(mfi);
378  const Array4<const Real>& eta_arr = (eta_src) ? eta_src->const_array(mfi) :
379  Array4<const Real>{};
380 
381  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
382  {
383  u_cc_arr(i,j,k) = myhalf * (u_arr(i,j,k) + u_arr(i+1,j ,k));
384  v_cc_arr(i,j,k) = myhalf * (v_arr(i,j,k) + v_arr(i ,j+1,k));
385 
386  Real theta = cons_arr(i,j,k,RhoTheta_comp) / cons_arr(i,j,k,Rho_comp);
387  fab_arr(i, j, k, 0) = cons_arr(i,j,k,Rho_comp);
388  fab_arr(i, j, k, 1) = theta;
389  Real ksgs = zero;
390  if (l_use_KE) {
391  ksgs = cons_arr(i,j,k,RhoKE_comp) / cons_arr(i,j,k,Rho_comp);
392  }
393  fab_arr(i, j, k, 2) = ksgs;
394  if (l_use_kturb) {
395  fab_arr(i, j, k, 3) = eta_arr(i,j,k,EddyDiff::Mom_v); // Kmv
396  fab_arr(i, j, k, 4) = eta_arr(i,j,k,EddyDiff::Theta_v); // Khv
397  } else {
398  fab_arr(i, j, k, 3) = zero;
399  fab_arr(i, j, k, 4) = zero;
400  }
401  fab_arr(i, j, k, 5) = u_cc_arr(i,j,k) * u_cc_arr(i,j,k); // u*u
402  fab_arr(i, j, k, 6) = u_cc_arr(i,j,k) * v_cc_arr(i,j,k); // u*v
403  fab_arr(i, j, k, 7) = v_cc_arr(i,j,k) * v_cc_arr(i,j,k); // v*v
404  fab_arr(i, j, k, 8) = u_cc_arr(i,j,k) * theta; // u*th
405  fab_arr(i, j, k, 9) = v_cc_arr(i,j,k) * theta; // v*th
406  fab_arr(i, j, k,10) = theta * theta; // th*th
407 
408  Real wcc = myhalf * (w_fc_arr(i,j,k) + w_fc_arr(i,j,k+1));
409 
410  // if the number of fields is changed above, then be sure to update
411  // the following def!
412  Real uiui = fab_arr(i,j,k,5) + fab_arr(i,j,k,7) + wcc*wcc;
413  fab_arr(i, j, k,11) = uiui * u_cc_arr(i,j,k); // (ui*ui)*u
414  fab_arr(i, j, k,12) = uiui * v_cc_arr(i,j,k); // (ui*ui)*v
415 
416  if (!use_moisture) {
417  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp));
418  p -= p0_arr(i,j,k);
419  fab_arr(i, j, k,13) = p; // p
420  fab_arr(i, j, k,14) = p * u_cc_arr(i,j,k); // p*u
421  fab_arr(i, j, k,15) = p * v_cc_arr(i,j,k); // p*v
422  fab_arr(i, j, k,16) = zero; // qv
423  fab_arr(i, j, k,17) = zero; // qc
424  fab_arr(i, j, k,18) = zero; // qr
425  fab_arr(i, j, k,19) = zero; // qi
426  fab_arr(i, j, k,20) = zero; // qs
427  fab_arr(i, j, k,21) = zero; // qg
428  }
429  });
430 
431  const Box& zbx = mfi.tilebox(IntVect(0,0,1));
432  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
433  {
434  // average to z faces (first to cell centers, then in z)
435  Real uface = fourth * ( u_arr(i ,j,k) + u_arr(i ,j,k-1)
436  + u_arr(i+1,j,k) + u_arr(i+1,j,k-1));
437  Real vface = fourth * ( v_arr(i,j ,k) + v_arr(i,j ,k-1)
438  + v_arr(i,j+1,k) + v_arr(i,j+1,k-1));
439  Real theta0 = cons_arr(i,j,k ,RhoTheta_comp) / cons_arr(i,j,k ,Rho_comp);
440  Real theta1 = cons_arr(i,j,k-1,RhoTheta_comp) / cons_arr(i,j,k-1,Rho_comp);
441  Real thface = myhalf*(theta0 + theta1);
442  fab_arr_stag(i,j,k,0) = uface * w_fc_arr(i,j,k); // u*w
443  fab_arr_stag(i,j,k,1) = vface * w_fc_arr(i,j,k); // v*w
444  fab_arr_stag(i,j,k,2) = w_fc_arr(i,j,k) * w_fc_arr(i,j,k); // w*w
445  fab_arr_stag(i,j,k,3) = thface * w_fc_arr(i,j,k); // th*w
446  Real uiui = uface*uface + vface*vface + fab_arr_stag(i,j,k,2);
447  fab_arr_stag(i,j,k,4) = uiui * w_fc_arr(i,j,k); // (ui*ui)*w
448  if (!use_moisture) {
449  Real p0 = getPgivenRTh(cons_arr(i, j, k , RhoTheta_comp)) - p0_arr(i,j,k );
450  Real p1 = getPgivenRTh(cons_arr(i, j, k-1, RhoTheta_comp)) - p0_arr(i,j,k-1);
451  Real pface = myhalf * (p0 + p1);
452  fab_arr_stag(i,j,k,5) = pface * w_fc_arr(i,j,k); // p*w
453  fab_arr_stag(i,j,k,6) = zero; // w*qv
454  fab_arr_stag(i,j,k,7) = zero; // w*qc
455  fab_arr_stag(i,j,k,8) = zero; // w*qr
456  fab_arr_stag(i,j,k,9) = zero; // w*thv
457  }
458  });
459 
460  } // mfi
461 
462  if (use_moisture)
463  {
464  int n_qstate_moist = micro->Get_Qstate_Moist_Size();
465 
466  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
467  {
468  const Box& bx = mfi.tilebox();
469  const Array4<Real>& fab_arr = mf_out.array(mfi);
470  const Array4<Real>& fab_arr_stag = mf_out_stag.array(mfi);
471  const Array4<Real>& cons_arr = mf_cons.array(mfi);
472  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
473  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
474  const Array4<Real>& w_fc_arr = w_fc.array(mfi);
475  const Array4<Real>& p0_arr = p_hse.array(mfi);
476 
477  int rhoqr_comp = solverChoice.moisture_indices.qr;
478 
479  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
480  {
481  Real qv = cons_arr(i,j,k,RhoQ1_comp) / cons_arr(i,j,k,Rho_comp);
482  Real qc = cons_arr(i,j,k,RhoQ2_comp) / cons_arr(i,j,k,Rho_comp);
483  Real qr = (rhoqr_comp > -1) ? cons_arr(i,j,k,rhoqr_comp) / cons_arr(i,j,k,Rho_comp) :
484  zero;
485  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp), qv);
486 
487  p -= p0_arr(i,j,k);
488  fab_arr(i, j, k,13) = p; // p
489  fab_arr(i, j, k,14) = p * u_cc_arr(i,j,k); // p*u
490  fab_arr(i, j, k,15) = p * v_cc_arr(i,j,k); // p*v
491  fab_arr(i, j, k,16) = qv; // qv
492  fab_arr(i, j, k,17) = qc; // qc
493  fab_arr(i, j, k,18) = qr; // qr
494  if (n_qstate_moist > 3) { // SAM model
495  fab_arr(i, j, k,19) = cons_arr(i,j,k,RhoQ3_comp) / cons_arr(i,j,k,Rho_comp); // qi
496  fab_arr(i, j, k,20) = cons_arr(i,j,k,RhoQ5_comp) / cons_arr(i,j,k,Rho_comp); // qs
497  fab_arr(i, j, k,21) = cons_arr(i,j,k,RhoQ6_comp) / cons_arr(i,j,k,Rho_comp); // qg
498  } else {
499  fab_arr(i, j, k,19) = zero; // qi
500  fab_arr(i, j, k,20) = zero; // qs
501  fab_arr(i, j, k,21) = zero; // qg
502  }
503  });
504 
505  const Box& zbx = mfi.tilebox(IntVect(0,0,1));
506  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
507  {
508  Real qv0 = cons_arr(i,j,k ,RhoQ1_comp) / cons_arr(i,j,k ,Rho_comp);
509  Real qv1 = cons_arr(i,j,k-1,RhoQ1_comp) / cons_arr(i,j,k-1,Rho_comp);
510  Real qc0 = cons_arr(i,j,k ,RhoQ2_comp) / cons_arr(i,j,k ,Rho_comp);
511  Real qc1 = cons_arr(i,j,k-1,RhoQ2_comp) / cons_arr(i,j,k-1,Rho_comp);
512  Real qr0 = (rhoqr_comp > -1) ? cons_arr(i,j,k ,RhoQ3_comp) / cons_arr(i,j,k ,Rho_comp) :
513  zero;
514  Real qr1 = (rhoqr_comp > -1) ? cons_arr(i,j,k-1,RhoQ3_comp) / cons_arr(i,j,k-1,Rho_comp) :
515  zero;
516  Real qvface = myhalf * (qv0 + qv1);
517  Real qcface = myhalf * (qc0 + qc1);
518  Real qrface = myhalf * (qr0 + qr1);
519 
520  Real p0 = getPgivenRTh(cons_arr(i, j, k , RhoTheta_comp), qv0) - p0_arr(i,j,k );
521  Real p1 = getPgivenRTh(cons_arr(i, j, k-1, RhoTheta_comp), qv1) - p0_arr(i,j,k-1);
522  Real pface = myhalf * (p0 + p1);
523 
524  Real theta0 = cons_arr(i,j,k ,RhoTheta_comp) / cons_arr(i,j,k ,Rho_comp);
525  Real theta1 = cons_arr(i,j,k-1,RhoTheta_comp) / cons_arr(i,j,k-1,Rho_comp);
526  Real thface = myhalf*(theta0 + theta1);
527  Real ql = qcface + qrface;
528  Real thv = thface * (1 + Real(0.61)*qvface - ql);
529 
530  fab_arr_stag(i,j,k,5) = pface * w_fc_arr(i,j,k); // p*w
531  fab_arr_stag(i,j,k,6) = qvface * w_fc_arr(i,j,k); // w*qv
532  fab_arr_stag(i,j,k,7) = qcface * w_fc_arr(i,j,k); // w*qc
533  fab_arr_stag(i,j,k,8) = qrface * w_fc_arr(i,j,k); // w*qr
534  fab_arr_stag(i,j,k,9) = thv * w_fc_arr(i,j,k); // w*thv
535  });
536  } // mfi
537  } // use_moisture
538 
539  // Sum in the horizontal plane
540  h_avg_u = sumToLine(u_cc,0,1, domain,zdir);
541  h_avg_v = sumToLine(v_cc,0,1, domain,zdir);
542  h_avg_w = sumToLine(w_fc,0,1,stag_domain,zdir);
543 
544  h_avg_rho = sumToLine(mf_out, 0,1,domain,zdir);
545  h_avg_th = sumToLine(mf_out, 1,1,domain,zdir);
546  h_avg_ksgs = sumToLine(mf_out, 2,1,domain,zdir);
547  h_avg_Kmv = sumToLine(mf_out, 3,1,domain,zdir);
548  h_avg_Khv = sumToLine(mf_out, 4,1,domain,zdir);
549  h_avg_uu = sumToLine(mf_out, 5,1,domain,zdir);
550  h_avg_uv = sumToLine(mf_out, 6,1,domain,zdir);
551  h_avg_vv = sumToLine(mf_out, 7,1,domain,zdir);
552  h_avg_uth = sumToLine(mf_out, 8,1,domain,zdir);
553  h_avg_vth = sumToLine(mf_out, 9,1,domain,zdir);
554  h_avg_thth = sumToLine(mf_out,10,1,domain,zdir);
555  h_avg_uiuiu = sumToLine(mf_out,11,1,domain,zdir);
556  h_avg_uiuiv = sumToLine(mf_out,12,1,domain,zdir);
557  h_avg_p = sumToLine(mf_out,13,1,domain,zdir);
558  h_avg_pu = sumToLine(mf_out,14,1,domain,zdir);
559  h_avg_pv = sumToLine(mf_out,15,1,domain,zdir);
560  h_avg_qv = sumToLine(mf_out,16,1,domain,zdir);
561  h_avg_qc = sumToLine(mf_out,17,1,domain,zdir);
562  h_avg_qr = sumToLine(mf_out,18,1,domain,zdir);
563  h_avg_qi = sumToLine(mf_out,19,1,domain,zdir);
564  h_avg_qs = sumToLine(mf_out,20,1,domain,zdir);
565  h_avg_qg = sumToLine(mf_out,21,1,domain,zdir);
566 
567  h_avg_uw = sumToLine(mf_out_stag,0,1,stag_domain,zdir);
568  h_avg_vw = sumToLine(mf_out_stag,1,1,stag_domain,zdir);
569  h_avg_ww = sumToLine(mf_out_stag,2,1,stag_domain,zdir);
570  h_avg_wth = sumToLine(mf_out_stag,3,1,stag_domain,zdir);
571  h_avg_uiuiw = sumToLine(mf_out_stag,4,1,stag_domain,zdir);
572  h_avg_pw = sumToLine(mf_out_stag,5,1,stag_domain,zdir);
573  h_avg_wqv = sumToLine(mf_out_stag,6,1,stag_domain,zdir);
574  h_avg_wqc = sumToLine(mf_out_stag,7,1,stag_domain,zdir);
575  h_avg_wqr = sumToLine(mf_out_stag,8,1,stag_domain,zdir);
576  h_avg_wthv = sumToLine(mf_out_stag,9,1,stag_domain,zdir);
577 
578  // Divide by the total number of cells we are averaging over
579  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
580  int unstag_size = h_avg_w.size() - 1; // _un_staggered heights
581  for (int k = 0; k < unstag_size; ++k) {
582  h_avg_u[k] /= area_z;
583  h_avg_v[k] /= area_z;
584  h_avg_rho[k] /= area_z;
585  h_avg_ksgs[k] /= area_z;
586  h_avg_Kmv[k] /= area_z;
587  h_avg_Khv[k] /= area_z;
588  h_avg_th[k] /= area_z;
589  h_avg_thth[k] /= area_z;
590  h_avg_uu[k] /= area_z;
591  h_avg_uv[k] /= area_z;
592  h_avg_vv[k] /= area_z;
593  h_avg_uth[k] /= area_z;
594  h_avg_vth[k] /= area_z;
595  h_avg_uiuiu[k] /= area_z;
596  h_avg_uiuiv[k] /= area_z;
597  h_avg_p[k] /= area_z;
598  h_avg_pu[k] /= area_z;
599  h_avg_pv[k] /= area_z;
600  h_avg_qv[k] /= area_z;
601  h_avg_qc[k] /= area_z;
602  h_avg_qr[k] /= area_z;
603  h_avg_qi[k] /= area_z;
604  h_avg_qs[k] /= area_z;
605  h_avg_qg[k] /= area_z;
606  }
607 
608  for (int k = 0; k < unstag_size+1; ++k) { // staggered heights
609  h_avg_w[k] /= area_z;
610  h_avg_uw[k] /= area_z;
611  h_avg_vw[k] /= area_z;
612  h_avg_ww[k] /= area_z;
613  h_avg_wth[k] /= area_z;
614  h_avg_uiuiw[k] /= area_z;
615  h_avg_pw[k] /= area_z;
616  h_avg_wqv[k] /= area_z;
617  h_avg_wqc[k] /= area_z;
618  h_avg_wqr[k] /= area_z;
619  h_avg_wthv[k] /= area_z;
620  }
621 }
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
const Box zbx
Definition: ERF_SetupDiff.H:9
real(kind=kind_phys), save qc0
Definition: ERF_module_mp_wsm6.F90:46
Here is the call graph for this function:

◆ derive_stress_profiles()

void ERF::derive_stress_profiles ( amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau11,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau12,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau13,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau22,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau23,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau33,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_hfx3,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_q1fx3,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_q2fx3,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_diss 
)
499 {
500  int lev = 0;
501 
502  // This will hold the stress tensor components
503  MultiFab mf_out(grids[lev], dmap[lev], 10, 0);
504 
505  MultiFab mf_rho(vars_new[lev][Vars::cons], make_alias, 0, 1);
506 
507  bool l_use_moist = ( solverChoice.moisture_type != MoistureType::None );
508 
509  for ( MFIter mfi(mf_out,TilingIfNotGPU()); mfi.isValid(); ++mfi)
510  {
511  const Box& bx = mfi.tilebox();
512  const Array4<Real>& fab_arr = mf_out.array(mfi);
513 
514  const Array4<const Real>& rho_arr = mf_rho.const_array(mfi);
515 
516  // NOTE: These are from the last RK stage...
517  const Array4<const Real>& tau11_arr = Tau[lev][TauType::tau11]->const_array(mfi);
518  const Array4<const Real>& tau12_arr = Tau[lev][TauType::tau12]->const_array(mfi);
519  const Array4<const Real>& tau13_arr = Tau[lev][TauType::tau13]->const_array(mfi);
520  const Array4<const Real>& tau22_arr = Tau[lev][TauType::tau22]->const_array(mfi);
521  const Array4<const Real>& tau23_arr = Tau[lev][TauType::tau23]->const_array(mfi);
522  const Array4<const Real>& tau33_arr = Tau[lev][TauType::tau33]->const_array(mfi);
523 
524  // These should be re-calculated during ERF_slow_rhs_post
525  // -- just vertical SFS kinematic heat flux for now
526  //const Array4<const Real>& hfx1_arr = SFS_hfx1_lev[lev]->const_array(mfi);
527  //const Array4<const Real>& hfx2_arr = SFS_hfx2_lev[lev]->const_array(mfi);
528  const Array4<const Real>& hfx3_arr = SFS_hfx3_lev[lev]->const_array(mfi);
529  const Array4<const Real>& q1fx3_arr = (l_use_moist) ? SFS_q1fx3_lev[lev]->const_array(mfi) :
530  Array4<const Real>{};
531  const Array4<const Real>& q2fx3_arr = (l_use_moist) ? SFS_q2fx3_lev[lev]->const_array(mfi) :
532  Array4<const Real>{};
533  const Array4<const Real>& diss_arr = SFS_diss_lev[lev]->const_array(mfi);
534 
535  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
536  {
537  // rho averaging should follow Diffusion/ERF_ComputeStress_*.cpp
538  fab_arr(i, j, k, 0) = tau11_arr(i,j,k) / rho_arr(i,j,k);
539  fab_arr(i, j, k, 1) = ( tau12_arr(i,j ,k) + tau12_arr(i+1,j ,k)
540  + tau12_arr(i,j+1,k) + tau12_arr(i+1,j+1,k) )
541  / ( rho_arr(i,j ,k) + rho_arr(i+1,j ,k)
542  + rho_arr(i,j+1,k) + rho_arr(i+1,j+1,k) );
543  fab_arr(i, j, k, 2) = ( tau13_arr(i,j,k ) + tau13_arr(i+1,j,k )
544  + tau13_arr(i,j,k+1) + tau13_arr(i+1,j,k+1) )
545  / ( rho_arr(i,j,k ) + rho_arr(i+1,j,k )
546  + rho_arr(i,j,k+1) + rho_arr(i+1,j,k+1) );
547  fab_arr(i, j, k, 3) = tau22_arr(i,j,k) / rho_arr(i,j,k);
548  fab_arr(i, j, k, 4) = ( tau23_arr(i,j,k ) + tau23_arr(i,j+1,k )
549  + tau23_arr(i,j,k+1) + tau23_arr(i,j+1,k+1) )
550  / ( rho_arr(i,j,k ) + rho_arr(i,j+1,k )
551  + rho_arr(i,j,k+1) + rho_arr(i,j+1,k+1) );
552  fab_arr(i, j, k, 5) = tau33_arr(i,j,k) / rho_arr(i,j,k);
553  fab_arr(i, j, k, 6) = myhalf * ( hfx3_arr(i,j,k) + hfx3_arr(i,j,k+1) ) / rho_arr(i,j,k);
554  fab_arr(i, j, k, 7) = (l_use_moist) ? myhalf * ( q1fx3_arr(i,j,k) + q1fx3_arr(i,j,k+1) ) / rho_arr(i,j,k) : zero;
555  fab_arr(i, j, k, 8) = (l_use_moist) ? myhalf * ( q2fx3_arr(i,j,k) + q2fx3_arr(i,j,k+1) ) / rho_arr(i,j,k) : zero;
556  fab_arr(i, j, k, 9) = diss_arr(i,j,k) / rho_arr(i,j,k);
557  });
558  }
559 
560  int zdir = 2;
561  auto domain = geom[0].Domain();
562 
563  h_avg_tau11 = sumToLine(mf_out,0,1,domain,zdir);
564  h_avg_tau12 = sumToLine(mf_out,1,1,domain,zdir);
565  h_avg_tau13 = sumToLine(mf_out,2,1,domain,zdir);
566  h_avg_tau22 = sumToLine(mf_out,3,1,domain,zdir);
567  h_avg_tau23 = sumToLine(mf_out,4,1,domain,zdir);
568  h_avg_tau33 = sumToLine(mf_out,5,1,domain,zdir);
569  h_avg_hfx3 = sumToLine(mf_out,6,1,domain,zdir);
570  h_avg_q1fx3 = sumToLine(mf_out,7,1,domain,zdir);
571  h_avg_q2fx3 = sumToLine(mf_out,8,1,domain,zdir);
572  h_avg_diss = sumToLine(mf_out,9,1,domain,zdir);
573 
574  int ht_size = h_avg_tau11.size();
575 
576  // Divide by the total number of cells we are averaging over
577  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
578  for (int k = 0; k < ht_size; ++k) {
579  h_avg_tau11[k] /= area_z;
580  h_avg_tau12[k] /= area_z;
581  h_avg_tau13[k] /= area_z;
582  h_avg_tau22[k] /= area_z;
583  h_avg_tau23[k] /= area_z;
584  h_avg_tau33[k] /= area_z;
585  h_avg_hfx3[k] /= area_z;
586  h_avg_q1fx3[k] /= area_z;
587  h_avg_q2fx3[k] /= area_z;
588  h_avg_diss[k] /= area_z;
589  }
590 }
auto rho_arr
Definition: ERF_UpdateWSubsidence_SineMassFlux.H:3
Here is the call graph for this function:

◆ derive_stress_profiles_stag()

void ERF::derive_stress_profiles_stag ( amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau11,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau12,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau13,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau22,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau23,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tau33,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_hfx3,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_q1fx3,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_q2fx3,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_diss 
)
629 {
630  int lev = 0;
631 
632  // This will hold the stress tensor components
633  MultiFab mf_out(grids[lev], dmap[lev], 10, 0);
634 
635  // This will hold Tau13 and Tau23
636  MultiFab mf_out_stag(convert(grids[lev], IntVect(0,0,1)), dmap[lev], 5, 0);
637 
638  MultiFab mf_rho(vars_new[lev][Vars::cons], make_alias, 0, 1);
639 
640  bool l_use_moist = ( solverChoice.moisture_type != MoistureType::None );
641 
642  for ( MFIter mfi(mf_out,TilingIfNotGPU()); mfi.isValid(); ++mfi)
643  {
644  const Box& bx = mfi.tilebox();
645  const Array4<Real>& fab_arr = mf_out.array(mfi);
646  const Array4<Real>& fab_arr_stag = mf_out_stag.array(mfi);
647 
648  const Array4<const Real>& rho_arr = mf_rho.const_array(mfi);
649 
650  // NOTE: These are from the last RK stage...
651  const Array4<const Real>& tau11_arr = Tau[lev][TauType::tau11]->const_array(mfi);
652  const Array4<const Real>& tau12_arr = Tau[lev][TauType::tau12]->const_array(mfi);
653  const Array4<const Real>& tau13_arr = Tau[lev][TauType::tau13]->const_array(mfi);
654  const Array4<const Real>& tau22_arr = Tau[lev][TauType::tau22]->const_array(mfi);
655  const Array4<const Real>& tau23_arr = Tau[lev][TauType::tau23]->const_array(mfi);
656  const Array4<const Real>& tau33_arr = Tau[lev][TauType::tau33]->const_array(mfi);
657 
658  // These should be re-calculated during ERF_slow_rhs_post
659  // -- just vertical SFS kinematic heat flux for now
660  //const Array4<const Real>& hfx1_arr = SFS_hfx1_lev[lev]->const_array(mfi);
661  //const Array4<const Real>& hfx2_arr = SFS_hfx2_lev[lev]->const_array(mfi);
662  const Array4<const Real>& hfx3_arr = SFS_hfx3_lev[lev]->const_array(mfi);
663  const Array4<const Real>& q1fx3_arr = (l_use_moist) ? SFS_q1fx3_lev[lev]->const_array(mfi) :
664  Array4<const Real>{};
665  const Array4<const Real>& q2fx3_arr = (l_use_moist) ? SFS_q2fx3_lev[lev]->const_array(mfi) :
666  Array4<const Real>{};
667  const Array4<const Real>& diss_arr = SFS_diss_lev[lev]->const_array(mfi);
668 
669  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
670  {
671  // rho averaging should follow Diffusion/ERF_ComputeStress_*.cpp
672  fab_arr(i, j, k, 0) = tau11_arr(i,j,k) / rho_arr(i,j,k);
673  fab_arr(i, j, k, 1) = ( tau12_arr(i,j ,k) + tau12_arr(i+1,j ,k)
674  + tau12_arr(i,j+1,k) + tau12_arr(i+1,j+1,k) )
675  / ( rho_arr(i,j ,k) + rho_arr(i+1,j ,k)
676  + rho_arr(i,j+1,k) + rho_arr(i+1,j+1,k) );
677  fab_arr(i, j, k, 3) = tau22_arr(i,j,k) / rho_arr(i,j,k);
678  fab_arr(i, j, k, 5) = tau33_arr(i,j,k) / rho_arr(i,j,k);
679  fab_arr(i, j, k, 9) = diss_arr(i,j,k) / rho_arr(i,j,k);
680  });
681 
682  const Box& zbx = mfi.tilebox(IntVect(0,0,1));
683  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
684  {
685  Real rho_face = myhalf * (rho_arr(i,j,k-1) + rho_arr(i,j,k));
686  // average from edge to face center
687  fab_arr_stag(i,j,k,0) = myhalf*(tau13_arr(i,j,k) + tau13_arr(i+1,j ,k)) / rho_face;
688  fab_arr_stag(i,j,k,1) = myhalf*(tau23_arr(i,j,k) + tau23_arr(i ,j+1,k)) / rho_face;
689 
690  fab_arr_stag(i,j,k,2) = hfx3_arr(i,j,k) / rho_face;
691  fab_arr_stag(i,j,k,3) = (l_use_moist) ? q1fx3_arr(i,j,k) / rho_face : zero;
692  fab_arr_stag(i,j,k,4) = (l_use_moist) ? q2fx3_arr(i,j,k) / rho_face : zero;
693  });
694  }
695 
696  int zdir = 2;
697  auto domain = geom[0].Domain();
698  Box stag_domain = domain;
699  stag_domain.convert(IntVect(0,0,1));
700 
701  h_avg_tau11 = sumToLine(mf_out,0,1,domain,zdir);
702  h_avg_tau12 = sumToLine(mf_out,1,1,domain,zdir);
703 // h_avg_tau13 = sumToLine(mf_out,2,1,domain,zdir);
704  h_avg_tau22 = sumToLine(mf_out,3,1,domain,zdir);
705 // h_avg_tau23 = sumToLine(mf_out,4,1,domain,zdir);
706  h_avg_tau33 = sumToLine(mf_out,5,1,domain,zdir);
707 // h_avg_hfx3 = sumToLine(mf_out,6,1,domain,zdir);
708 // h_avg_q1fx3 = sumToLine(mf_out,7,1,domain,zdir);
709 // h_avg_q2fx3 = sumToLine(mf_out,8,1,domain,zdir);
710  h_avg_diss = sumToLine(mf_out,9,1,domain,zdir);
711 
712  h_avg_tau13 = sumToLine(mf_out_stag,0,1,stag_domain,zdir);
713  h_avg_tau23 = sumToLine(mf_out_stag,1,1,stag_domain,zdir);
714  h_avg_hfx3 = sumToLine(mf_out_stag,2,1,stag_domain,zdir);
715  h_avg_q1fx3 = sumToLine(mf_out_stag,3,1,stag_domain,zdir);
716  h_avg_q2fx3 = sumToLine(mf_out_stag,4,1,stag_domain,zdir);
717 
718  int ht_size = h_avg_tau11.size(); // _un_staggered
719 
720  // Divide by the total number of cells we are averaging over
721  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
722  for (int k = 0; k < ht_size; ++k) {
723  h_avg_tau11[k] /= area_z;
724  h_avg_tau12[k] /= area_z;
725  h_avg_tau13[k] /= area_z;
726  h_avg_tau22[k] /= area_z;
727  h_avg_tau23[k] /= area_z;
728  h_avg_tau33[k] /= area_z;
729  h_avg_hfx3[k] /= area_z;
730  h_avg_q1fx3[k] /= area_z;
731  h_avg_q2fx3[k] /= area_z;
732  h_avg_diss[k] /= area_z;
733  }
734  // staggered heights
735  h_avg_tau13[ht_size] /= area_z;
736  h_avg_tau23[ht_size] /= area_z;
737  h_avg_hfx3[ht_size] /= area_z;
738  h_avg_q1fx3[ht_size] /= area_z;
739  h_avg_q2fx3[ht_size] /= area_z;
740 }
Here is the call graph for this function:

◆ derive_upwp()

void ERF::derive_upwp ( amrex::Vector< amrex::Real > &  h_havg)

◆ EBFactory()

amrex::EBFArrayBoxFactory const& ERF::EBFactory ( int  lev) const
inlineprivatenoexcept
1739  {
1740  return *(eb[lev]->get_const_factory());
1741  }
amrex::Vector< std::unique_ptr< eb_ > > eb
Definition: ERF.H:1731

◆ erf_enforce_hse()

void ERF::erf_enforce_hse ( int  lev,
amrex::MultiFab &  dens,
amrex::MultiFab &  pres,
amrex::MultiFab &  pi,
amrex::MultiFab &  th,
amrex::MultiFab &  qv,
std::unique_ptr< amrex::MultiFab > &  z_cc 
)

Enforces hydrostatic equilibrium when using terrain.

Parameters
[in]levInteger specifying the current level
[out]densMultiFab storing base state density
[out]presMultiFab storing base state pressure
[out]piMultiFab storing base state Exner function
[in]z_ccPointer to MultiFab storing cell centered z-coordinates
213 {
214  Real l_gravity = solverChoice.gravity;
215  bool l_use_terrain = (solverChoice.mesh_type != MeshType::ConstantDz);
216 
217  const auto geomdata = geom[lev].data();
218  const Real dz = geomdata.CellSize(2);
219 
220  for ( MFIter mfi(dens, TileNoZ()); mfi.isValid(); ++mfi )
221  {
222  // Create a flat box with same horizontal extent but only one cell in vertical
223  const Box& tbz = mfi.nodaltilebox(2);
224  int klo = tbz.smallEnd(2);
225  int khi = tbz.bigEnd(2);
226 
227  // Note we only grow by 1 because that is how big z_cc is.
228  Box b2d = tbz; // Copy constructor
229  b2d.grow(0,1);
230  b2d.grow(1,1);
231  b2d.setRange(2,0);
232 
233  // Intersect this box with the domain
234  Box zdomain = convert(geom[lev].Domain(),tbz.ixType());
235  b2d &= zdomain;
236 
237  // We integrate to the first cell (and below) by using rho in this cell
238  // If gravity == 0 this is constant pressure
239  // If gravity != 0, hence this is a wall, this gives gp0 = dens[0] * gravity
240  // (dens_hse*gravity would also be dens[0]*gravity because we use foextrap for rho at k = -1)
241  // Note ng_pres_hse = 1
242 
243  // We start by assuming pressure on the ground is p_0 (in ERF_Constants.H)
244  // Note that gravity is positive
245 
246  Array4<Real> rho_arr = dens.array(mfi);
247  Array4<Real> pres_arr = pres.array(mfi);
248  Array4<Real> pi_arr = pi.array(mfi);
249  Array4<Real> th_arr = theta.array(mfi);
250  Array4<Real> zcc_arr;
251  if (l_use_terrain) {
252  zcc_arr = z_cc->array(mfi);
253  }
254 
255  const Real rdOcp = solverChoice.rdOcp;
256 
257  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int)
258  {
259  // Set value at surface from Newton iteration for rho
260  if (klo == 0)
261  {
262  // Physical height of the terrain at cell center
263  Real hz;
264  if (l_use_terrain) {
265  hz = zcc_arr(i,j,klo);
266  } else {
267  hz = myhalf*dz;
268  }
269 
270  pres_arr(i,j,klo) = p_0 - hz * rho_arr(i,j,klo) * l_gravity;
271  pi_arr(i,j,klo) = getExnergivenP(pres_arr(i,j,klo), rdOcp);
272  th_arr(i,j,klo) = getRhoThetagivenP(pres_arr(i,j,klo)) / rho_arr(i,j,klo);
273 
274  //
275  // Set ghost cell with dz and rho at boundary
276  // (We will set the rest of the ghost cells in the boundary condition routine)
277  //
278  pres_arr(i,j,klo-1) = p_0 + hz * rho_arr(i,j,klo) * l_gravity;
279  pi_arr(i,j,klo-1) = getExnergivenP(pres_arr(i,j,klo-1), rdOcp);
280  th_arr(i,j,klo-1) = getRhoThetagivenP(pres_arr(i,j,klo-1)) / rho_arr(i,j,klo-1);
281 
282  } else {
283 
284  // If level > 0 and klo > 0, we need to use the value of pres_arr(i,j,klo-1) which was
285  // filled from FillPatch-ing it.
286  Real dz_loc;
287  if (l_use_terrain) {
288  dz_loc = (zcc_arr(i,j,klo) - zcc_arr(i,j,klo-1));
289  } else {
290  dz_loc = dz;
291  }
292 
293  Real dens_interp = myhalf*(rho_arr(i,j,klo) + rho_arr(i,j,klo-1));
294  pres_arr(i,j,klo) = pres_arr(i,j,klo-1) - dz_loc * dens_interp * l_gravity;
295 
296  pi_arr(i,j,klo ) = getExnergivenP(pres_arr(i,j,klo ), rdOcp);
297  th_arr(i,j,klo ) = getRhoThetagivenP(pres_arr(i,j,klo )) / rho_arr(i,j,klo );
298 
299  pi_arr(i,j,klo-1) = getExnergivenP(pres_arr(i,j,klo-1), rdOcp);
300  th_arr(i,j,klo-1) = getRhoThetagivenP(pres_arr(i,j,klo-1)) / rho_arr(i,j,klo-1);
301  }
302 
303  Real dens_interp;
304  if (l_use_terrain) {
305  for (int k = klo+1; k <= khi; k++) {
306  Real dz_loc = (zcc_arr(i,j,k) - zcc_arr(i,j,k-1));
307  dens_interp = myhalf*(rho_arr(i,j,k) + rho_arr(i,j,k-1));
308  pres_arr(i,j,k) = pres_arr(i,j,k-1) - dz_loc * dens_interp * l_gravity;
309  pi_arr(i,j,k) = getExnergivenP(pres_arr(i,j,k), rdOcp);
310  th_arr(i,j,k) = getRhoThetagivenP(pres_arr(i,j,k)) / rho_arr(i,j,k);
311  }
312  } else {
313  for (int k = klo+1; k <= khi; k++) {
314  dens_interp = myhalf*(rho_arr(i,j,k) + rho_arr(i,j,k-1));
315  pres_arr(i,j,k) = pres_arr(i,j,k-1) - dz * dens_interp * l_gravity;
316  pi_arr(i,j,k) = getExnergivenP(pres_arr(i,j,k), rdOcp);
317  th_arr(i,j,k) = getRhoThetagivenP(pres_arr(i,j,k)) / rho_arr(i,j,k);
318  }
319  }
320  });
321 
322  } // mfi
323 
324  dens.FillBoundary(geom[lev].periodicity());
325  pres.FillBoundary(geom[lev].periodicity());
326  pi.FillBoundary(geom[lev].periodicity());
327  theta.FillBoundary(geom[lev].periodicity());
328  qv.FillBoundary(geom[lev].periodicity());
329 }
constexpr amrex::Real p_0
Definition: ERF_Constants.H:50
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhoThetagivenP(const amrex::Real p, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:172
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenP(const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:141
const Real rdOcp
Definition: ERF_InitCustomPert_Bomex.H:16
@ pres
Definition: ERF_Kessler.H:26
real(c_double), parameter, private pi
Definition: ERF_module_mp_morr_two_moment.F90:100
real(kind=kind_phys), parameter, private dens
Definition: ERF_module_mp_wsm6.F90:39
amrex::Real rdOcp
Definition: ERF_DataStruct.H:1301
Here is the call graph for this function:

◆ ERF_shared()

void ERF::ERF_shared ( )
62 {
63  if (ParallelDescriptor::IOProcessor()) {
64  const char* erf_hash = buildInfoGetGitHash(1);
65  const char* amrex_hash = buildInfoGetGitHash(2);
66  const char* buildgithash = buildInfoGetBuildGitHash();
67  const char* buildgitname = buildInfoGetBuildGitName();
68 
69  if (strlen(erf_hash) > 0) {
70  Print() << "\n"
71  << "ERF git hash: " << erf_hash << "\n";
72  }
73  if (strlen(amrex_hash) > 0) {
74  Print() << "AMReX git hash: " << amrex_hash << "\n";
75  }
76  if (strlen(buildgithash) > 0) {
77  Print() << buildgitname << " git hash: " << buildgithash << "\n";
78  }
79 
80  Print() << "\n";
81  }
82 
83  int nlevs_max = max_level + 1;
84 
85 #ifdef ERF_USE_WINDFARM
86  Nturb.resize(nlevs_max);
87  vars_windfarm.resize(nlevs_max);
88  SMark.resize(nlevs_max);
89 #endif
90 
91  qheating_rates.resize(nlevs_max);
92  rad_fluxes.resize(nlevs_max);
93 
94  // NOTE: size lsm before readparams (chooses the model at all levels)
95  lsm.ReSize(nlevs_max);
96  lsm_data.resize(nlevs_max);
97  lsm_flux.resize(nlevs_max);
98 
99  rhotheta_src.resize(nlevs_max);
100  rhoqt_src.resize(nlevs_max);
101 
102  // NOTE: size canopy model before readparams (if file exists, we construct)
103  m_forest_drag.resize(nlevs_max);
104  for (int lev = 0; lev <= max_level; ++lev) { m_forest_drag[lev] = nullptr;}
105 
106  ReadParameters();
107  initializeMicrophysics(nlevs_max);
108 
109 #ifdef ERF_USE_WINDFARM
110  initializeWindFarm(nlevs_max);
111 #endif
112 
113 #ifdef ERF_USE_EAMXX_SHOC
114  eamxx_shoc_interface.resize(nlevs_max);
115  for (int lev = 0; lev <= max_level; ++lev) {
116  if (solverChoice.turbChoice[lev].uses_eamxx_shoc()) {
117  eamxx_shoc_interface[lev] = std::make_unique<SHOCInterface>(lev, solverChoice);
118  }
119  }
120 #endif
121 
122 #ifdef ERF_USE_NATIVE_SHOC
123  native_shoc_driver.resize(nlevs_max);
124  for (int lev = 0; lev <= max_level; ++lev) {
125  if (solverChoice.turbChoice[lev].uses_native_shoc()) {
126  native_shoc_driver[lev] = std::make_unique<ShocDriver>(lev, solverChoice);
127  }
128  }
129 #endif
130 
131  rad.resize(nlevs_max);
132  for (int lev = 0; lev <= max_level; ++lev) {
133  if (solverChoice.rad_type == RadiationType::RRTMGP) {
134 #ifdef ERF_USE_RRTMGP
135  rad[lev] = std::make_unique<Radiation>(lev, solverChoice);
136  // pass radiation datalog frequency to model - RRTMGP needs to know when to save data for profiles
137  rad[lev]->setDataLogFrequency(rad_datalog_int);
138 #endif
139  } else if (solverChoice.rad_type != RadiationType::None) {
140  Abort("Don't know this radiation model!");
141  }
142  }
143  const std::string& pv3d_1 = "plot_vars_1" ; setPlotVariables(pv3d_1,plot3d_var_names_1);
144  const std::string& pv3d_2 = "plot_vars_2" ; setPlotVariables(pv3d_2,plot3d_var_names_2);
145  const std::string& pv2d_1 = "plot2d_vars_1"; setPlotVariables2D(pv2d_1,plot2d_var_names_1);
146  const std::string& pv2d_2 = "plot2d_vars_2"; setPlotVariables2D(pv2d_2,plot2d_var_names_2);
147 
148  // This is only used when we have mesh_type == MeshType::StretchedDz
149  stretched_dz_h.resize(nlevs_max);
150  stretched_dz_d.resize(nlevs_max);
151 
152  // Initialize staggered vertical levels for grid stretching or terrain, and
153  // to simplify Rayleigh damping layer calculations.
154  zlevels_stag.resize(max_level+1);
158  geom,
159  refRatio(),
162  solverChoice.dz0);
163 
164  if (SolverChoice::mesh_type == MeshType::StretchedDz ||
165  SolverChoice::mesh_type == MeshType::VariableDz) {
166  int nz = geom[0].Domain().length(2) + 1; // staggered
167  if (std::fabs(zlevels_stag[0][nz-1]-geom[0].ProbHi(2)) > Real(1.0e-4)) {
168  Print() << "Note: prob_hi[2]=" << geom[0].ProbHi(2)
169  << " does not match highest requested z level " << zlevels_stag[0][nz-1]
170  << std::endl;
171  }
172  if (std::fabs(zlevels_stag[0][0]-geom[0].ProbLo(2)) > Real(1.0e-4)) {
173  Print() << "Note: prob_lo[2]=" << geom[0].ProbLo(2)
174  << " does not match lowest requested level " << zlevels_stag[0][0]
175  << std::endl;
176  }
177 
178  // Redefine the problem domain here?
179  }
180 
181  // Get lo/hi indices for massflux calc
183  if (solverChoice.mesh_type == MeshType::ConstantDz) {
186  const Real massflux_zlo = solverChoice.const_massflux_layer_lo - geom[0].ProbLo(2);
187  const Real massflux_zhi = solverChoice.const_massflux_layer_hi - geom[0].ProbLo(2);
188  const Real dz = geom[0].CellSize(2);
189  if (zlo_unset) {
190  solverChoice.massflux_klo = geom[0].Domain().smallEnd(2);
191  } else {
192  solverChoice.massflux_klo = static_cast<int>(std::ceil(massflux_zlo / dz - myhalf));
193  }
194  if (zhi_unset) {
195  solverChoice.massflux_khi = geom[0].Domain().bigEnd(2);
196  } else {
197  solverChoice.massflux_khi = static_cast<int>(std::floor(massflux_zhi / dz - myhalf));
198  }
199  } else if (solverChoice.mesh_type == MeshType::StretchedDz) {
200  const Real massflux_zlo = solverChoice.const_massflux_layer_lo;
201  const Real massflux_zhi = solverChoice.const_massflux_layer_hi;
202  solverChoice.massflux_klo = geom[0].Domain().smallEnd(2);
203  solverChoice.massflux_khi = geom[0].Domain().bigEnd(2) + 1;
204  for (int k=0; k <= geom[0].Domain().bigEnd(2)+1; ++k) {
205  if (zlevels_stag[0][k] <= massflux_zlo) solverChoice.massflux_klo = k;
206  if (zlevels_stag[0][k] <= massflux_zhi) solverChoice.massflux_khi = k;
207  }
208  } else { // solverChoice.mesh_type == MeshType::VariableDz
209  Error("Const massflux with variable dz not supported -- planar averages are on k rather than constant-z planes");
210  }
211 
212  Print() << "Constant mass flux based on k in ["
213  << solverChoice.massflux_klo << ", " << solverChoice.massflux_khi << "]" << std::endl;
214  }
215 
216 #ifdef ERF_REMORA_FORCE_PROBINIT_LINK
217  extern void erf_probinit_link_anchor_func () noexcept;
218  erf_probinit_link_anchor_func();
219 #endif
220  prob = amrex_probinit(geom[0].ProbLo(),geom[0].ProbHi());
221 
222  // Geometry on all levels has been defined already.
223 
224  // No valid BoxArray and DistributionMapping have been defined.
225  // But the arrays for them have been resized.
226 
227  t_new.resize(nlevs_max, zero);
228  t_old.resize(nlevs_max, -bogus_large_value);
229  dt.resize(nlevs_max, std::min(bogus_large_value,dt_max_initial));
230  dt_mri_ratio.resize(nlevs_max, 1);
231 
232  vars_new.resize(nlevs_max);
233  vars_old.resize(nlevs_max);
234  gradp.resize(nlevs_max);
235 
236  // We resize this regardless in order to pass it without error
237  pp_inc.resize(nlevs_max);
238 
239  // Used in the fast substepping only
240  lagged_delta_rt.resize(nlevs_max);
241  avg_xmom.resize(nlevs_max);
242  avg_ymom.resize(nlevs_max);
243  avg_zmom.resize(nlevs_max);
244 
245  rU_new.resize(nlevs_max);
246  rV_new.resize(nlevs_max);
247  rW_new.resize(nlevs_max);
248 
249  rU_old.resize(nlevs_max);
250  rV_old.resize(nlevs_max);
251  rW_old.resize(nlevs_max);
252 
253  // xmom_crse_rhs.resize(nlevs_max);
254  // ymom_crse_rhs.resize(nlevs_max);
255  zmom_crse_rhs.resize(nlevs_max);
256 
257  for (int lev = 0; lev < nlevs_max; ++lev) {
258  vars_new[lev].resize(Vars::NumTypes);
259  vars_old[lev].resize(Vars::NumTypes);
260  gradp[lev].resize(AMREX_SPACEDIM);
261  }
262 
263  // Time integrator
264  mri_integrator_mem.resize(nlevs_max);
265 
266  // Physical boundary conditions
267  physbcs_cons.resize(nlevs_max);
268  physbcs_u.resize(nlevs_max);
269  physbcs_v.resize(nlevs_max);
270  physbcs_w.resize(nlevs_max);
271  physbcs_base.resize(nlevs_max);
272 
273  // Planes to hold Dirichlet values at boundaries
274  xvel_bc_data.resize(nlevs_max);
275  yvel_bc_data.resize(nlevs_max);
276  zvel_bc_data.resize(nlevs_max);
277  th_bc_data.resize(nlevs_max);
278 
279  advflux_reg.resize(nlevs_max);
280 
281  // Stresses
282  Tau.resize(nlevs_max);
283  Tau_corr.resize(nlevs_max);
284  SFS_hfx1_lev.resize(nlevs_max); SFS_hfx2_lev.resize(nlevs_max); SFS_hfx3_lev.resize(nlevs_max);
285  SFS_diss_lev.resize(nlevs_max);
286  SFS_q1fx1_lev.resize(nlevs_max); SFS_q1fx2_lev.resize(nlevs_max); SFS_q1fx3_lev.resize(nlevs_max);
287  SFS_q2fx3_lev.resize(nlevs_max);
288  eddyDiffs_lev.resize(nlevs_max);
289  SmnSmn_lev.resize(nlevs_max);
290  Tau_EB.resize(nlevs_max);
291  hfx3_EB.resize(nlevs_max);
292 
293  // Sea surface temps
294  sst_lev.resize(nlevs_max);
295  tsk_lev.resize(nlevs_max);
296  lmask_lev.resize(nlevs_max);
297 
298  // Land and soil grid type and urban fractions
299  land_type_lev.resize(nlevs_max);
300  soil_type_lev.resize(nlevs_max);
301  urb_frac_lev.resize(nlevs_max);
302 
303  // Metric terms
304  z_phys_nd.resize(nlevs_max);
305  z_phys_cc.resize(nlevs_max);
306  detJ_cc.resize(nlevs_max);
307  ax.resize(nlevs_max);
308  ay.resize(nlevs_max);
309  az.resize(nlevs_max);
310 
311  z_phys_nd_new.resize(nlevs_max);
312  detJ_cc_new.resize(nlevs_max);
313 
314  z_phys_nd_src.resize(nlevs_max);
315  z_phys_cc_src.resize(nlevs_max);
316  detJ_cc_src.resize(nlevs_max);
317  ax_src.resize(nlevs_max);
318  ay_src.resize(nlevs_max);
319  az_src.resize(nlevs_max);
320 
321  z_t_rk.resize(nlevs_max);
322 
323  terrain_blanking.resize(nlevs_max);
324 
325  // Wall distance
326  walldist.resize(nlevs_max);
327 
328  // BoxArrays to make MultiFabs needed to convert WRFBdy data
329  ba1d.resize(nlevs_max);
330  ba2d.resize(nlevs_max);
331 
332  // MultiFabs needed to convert WRFBdy data
333  mf_PSFC.resize(nlevs_max);
334 
335  // Map factors
336  mapfac.resize(nlevs_max);
337 
338  // Fine mask
339  fine_mask.resize(nlevs_max);
340 
341  // Thin immersed body
342  xflux_imask.resize(nlevs_max);
343  yflux_imask.resize(nlevs_max);
344  zflux_imask.resize(nlevs_max);
345  //overset_imask.resize(nlevs_max);
346  thin_xforce.resize(nlevs_max);
347  thin_yforce.resize(nlevs_max);
348  thin_zforce.resize(nlevs_max);
349 
350  // Base state
351  base_state.resize(nlevs_max);
352  base_state_new.resize(nlevs_max);
353 
354  // Wave coupling data
355  Hwave.resize(nlevs_max);
356  Lwave.resize(nlevs_max);
357  for (int lev = 0; lev < max_level; ++lev)
358  {
359  Hwave[lev] = nullptr;
360  Lwave[lev] = nullptr;
361  }
362  Hwave_onegrid.resize(nlevs_max);
363  Lwave_onegrid.resize(nlevs_max);
364  for (int lev = 0; lev < max_level; ++lev)
365  {
366  Hwave_onegrid[lev] = nullptr;
367  Lwave_onegrid[lev] = nullptr;
368  }
369 
370  // Theta prim for MOST
371  Theta_prim.resize(nlevs_max);
372 
373  // Qv prim for MOST
374  Qv_prim.resize(nlevs_max);
375 
376  // Qr prim for MOST
377  Qr_prim.resize(nlevs_max);
378 
379  // Time averaged velocity field
380  vel_t_avg.resize(nlevs_max);
381  t_avg_cnt.resize(nlevs_max);
382 
383  // Size lat long arrays and default to null pointers
384  lat_m.resize(nlevs_max);
385  lon_m.resize(nlevs_max);
386  for (int lev = 0; lev < max_level; ++lev) {
387  lat_m[lev] = nullptr;
388  lon_m[lev] = nullptr;
389  }
390 
391  // Variable coriolis
392  sinPhi_m.resize(nlevs_max);
393  cosPhi_m.resize(nlevs_max);
394  for (int lev = 0; lev < max_level; ++lev) {
395  sinPhi_m[lev] = nullptr;
396  cosPhi_m[lev] = nullptr;
397  }
398 
399  // Rayleigh damping
400  h_rayleigh_ptrs.resize(nlevs_max);
401  d_rayleigh_ptrs.resize(nlevs_max);
402  h_sinesq_ptrs.resize(nlevs_max);
403  d_sinesq_ptrs.resize(nlevs_max);
404  h_sinesq_stag_ptrs.resize(nlevs_max);
405  d_sinesq_stag_ptrs.resize(nlevs_max);
406 
407  // Initialize tagging criteria for mesh refinement
409 
410  for (int lev = 0; lev < max_level; ++lev)
411  {
412  Print() << "Refinement ratio at level " << lev+1 << " set to be " <<
413  ref_ratio[lev][0] << " " << ref_ratio[lev][1] << " " << ref_ratio[lev][2] << std::endl;
414  }
415 
416  // We will create each of these in MakeNewLevelFromScratch
417  eb.resize(max_level+1);
418  for (int lev = 0; lev < max_level + 1; lev++){
419  eb[lev] = std::make_unique<eb_>();
420  }
421 
422  //
423  // Construct the EB data structures and store in a separate class
424  //
425  // This is needed before initializing level MultiFabs
426  if ( solverChoice.terrain_type == TerrainType::EB ||
427  solverChoice.terrain_type == TerrainType::ImmersedForcing)
428  {
429  std::string geometry ="terrain";
430  ParmParse pp_eb2("eb2");
431  pp_eb2.queryAdd("geometry", geometry);
432 
433  constexpr int ngrow_for_eb = 4; // This is the default in amrex but we need to explicitly pass it here since
434  // we want to also pass the build_coarse_level_by_coarsening argument
435  const bool build_eb_for_multigrid = (solverChoice.terrain_type == TerrainType::EB &&
437  solverChoice.anelastic[0] == 1));
438  // Note this just needs to be an integer > number of V-cycles one might use
439  const int max_coarsening_level = (build_eb_for_multigrid) ? 100 : 0;
440  const bool build_coarse_level_by_coarsening(false);
441 
442  // Define GeometryShop using the implicit function
443  if (geometry == "terrain") {
444  Box terrain_bx(surroundingNodes(geom[max_level].Domain())); terrain_bx.grow(3);
445  FArrayBox terrain_fab(makeSlab(terrain_bx,2,0),1);
446  Real dummy_time = zero;
447  prob->init_terrain_surface(geom[max_level], terrain_fab, dummy_time);
448  TerrainIF implicit_fun(terrain_fab, geom[max_level], stretched_dz_d[max_level]);
449  auto gshop = EB2::makeShop(implicit_fun);
450  if (build_eb_for_multigrid) {
451  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
452  ngrow_for_eb, build_coarse_level_by_coarsening);
453  } else {
454  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
455  }
456  } else if (geometry == "plane") {
457  RealArray plane_point{zero, zero, zero};
458  RealArray plane_normal{zero, zero, -one}; // pointing into the solid region
459  pp_eb2.query("plane_point", plane_point);
460  pp_eb2.query("plane_normal", plane_normal);
461  EB2::PlaneIF implicit_fun(plane_point, plane_normal, true);
462  auto gshop = EB2::makeShop(implicit_fun);
463  if (build_eb_for_multigrid) {
464  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
465  ngrow_for_eb, build_coarse_level_by_coarsening);
466  } else {
467  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
468  }
469  } else if (geometry == "box") {
470  RealArray box_lo{zero, zero, zero};
471  RealArray box_hi{zero, zero, zero};
472  pp_eb2.query("box_lo", box_lo);
473  pp_eb2.query("box_hi", box_hi);
474  EB2::BoxIF implicit_fun(box_lo, box_hi, false);
475  auto gshop = EB2::makeShop(implicit_fun);
476  if (build_eb_for_multigrid) {
477  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
478  ngrow_for_eb, build_coarse_level_by_coarsening);
479  } else {
480  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
481  }
482  } else if (geometry == "sphere") {
483  auto ProbLoArr = geom[max_level].ProbLoArray();
484  auto ProbHiArr = geom[max_level].ProbHiArray();
485  const Real xcen = myhalf * (ProbLoArr[0] + ProbHiArr[0]);
486  const Real ycen = myhalf * (ProbLoArr[1] + ProbHiArr[1]);
487  RealArray sphere_center = {xcen, ycen, zero};
488  EB2::SphereIF implicit_fun(myhalf, sphere_center, false);
489  auto gshop = EB2::makeShop(implicit_fun);
490  if (build_eb_for_multigrid) {
491  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
492  ngrow_for_eb, build_coarse_level_by_coarsening);
493  } else {
494  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
495  }
496  }
497  }
498 
499  if ( solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
500  constexpr int ngrow_for_eb = 4;
501  Box buildings_bx(surroundingNodes(geom[max_level].Domain())); buildings_bx.grow(3);
502  FArrayBox buildings_fab(makeSlab(buildings_bx,2,0),1);
503  Real dummy_time = zero;
504  prob->init_buildings_surface(geom[max_level], buildings_fab, dummy_time);
505  TerrainIF implicit_fun(buildings_fab, geom[max_level], stretched_dz_d[max_level]);
506  auto gshop = EB2::makeShop(implicit_fun);
507  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
508  }
509 
510  forecast_state_1.resize(nlevs_max);
511  forecast_state_2.resize(nlevs_max);
512  forecast_state_interp.resize(nlevs_max);
513 
514  surface_state_1.resize(nlevs_max);
515  surface_state_2.resize(nlevs_max);
516  surface_state_interp.resize(nlevs_max);
517 }
void init_zlevels(Vector< Vector< Real >> &zlevels_stag, Vector< Vector< Real >> &stretched_dz_h, Vector< Gpu::DeviceVector< Real >> &stretched_dz_d, Vector< Geometry > const &geom, Vector< IntVect > const &ref_ratio, const Real grid_stretching_ratio, const Real zsurf, const Real dz0)
Definition: ERF_InitZLevels.cpp:11
std::unique_ptr< ProblemBase > amrex_probinit(const amrex_real *problo, const amrex_real *probhi) AMREX_ATTRIBUTE_WEAK
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Hwave_onegrid
Definition: ERF.H:1070
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_yforce
Definition: ERF.H:1106
void setPlotVariables(const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile.cpp:14
amrex::Vector< amrex::Vector< amrex::MultiFab > > gradp
Definition: ERF.H:908
void ReadParameters()
Definition: ERF.cpp:1950
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_interp
Definition: ERF.H:177
amrex::Vector< std::unique_ptr< amrex::MultiFab > > mf_PSFC
Definition: ERF.H:1370
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd_src
Definition: ERF.H:1037
amrex::Vector< amrex::MultiFab > base_state_new
Definition: ERF.H:1065
amrex::Vector< std::unique_ptr< amrex::MultiFab > > az
Definition: ERF.H:1035
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking
Definition: ERF.H:1050
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd_new
Definition: ERF.H:1044
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_zforce
Definition: ERF.H:1107
amrex::Vector< std::string > plot3d_var_names_2
Definition: ERF.H:1204
amrex::Vector< std::string > plot2d_var_names_1
Definition: ERF.H:1205
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_xforce
Definition: ERF.H:1105
void setPlotVariables2D(const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile2D.cpp:93
amrex::Vector< amrex::Real > t_new
Definition: ERF.H:896
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > th_bc_data
Definition: ERF.H:863
amrex::Vector< amrex::MultiFab > surface_state_1
Definition: ERF.H:178
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_t_rk
Definition: ERF.H:1047
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Lwave_onegrid
Definition: ERF.H:1071
amrex::Vector< amrex::Vector< amrex::Real > > h_sinesq_ptrs
Definition: ERF.H:1419
amrex::Vector< std::unique_ptr< amrex::MultiFab > > fine_mask
Definition: ERF.H:1059
amrex::Vector< std::unique_ptr< ForestDrag > > m_forest_drag
Definition: ERF.H:1447
amrex::Vector< amrex::BoxArray > ba1d
Definition: ERF.H:1360
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > xvel_bc_data
Definition: ERF.H:860
int rad_datalog_int
Definition: ERF.H:987
amrex::Vector< amrex::MultiFab > surface_state_2
Definition: ERF.H:179
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc_src
Definition: ERF.H:1039
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ay_src
Definition: ERF.H:1041
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > yflux_imask
Definition: ERF.H:1100
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_flux
Definition: ERF.H:969
amrex::Vector< std::string > plot3d_var_names_1
Definition: ERF.H:1203
void refinement_criteria_setup()
Definition: ERF_Tagging.cpp:472
amrex::Vector< std::string > plot2d_var_names_2
Definition: ERF.H:1206
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > Tau_corr
Definition: ERF.H:999
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ax_src
Definition: ERF.H:1040
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > urb_frac_lev
Definition: ERF.H:1011
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_cc_src
Definition: ERF.H:1038
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_2
Definition: ERF.H:176
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > soil_type_lev
Definition: ERF.H:1010
amrex::Vector< amrex::Vector< amrex::Real > > zlevels_stag
Definition: ERF.H:1026
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_data
Definition: ERF.H:967
amrex::Vector< amrex::Vector< amrex::Real > > stretched_dz_h
Definition: ERF.H:1061
amrex::Vector< std::unique_ptr< amrex::MultiFab > > az_src
Definition: ERF.H:1042
static amrex::Real dt_max_initial
Definition: ERF.H:1153
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Lwave
Definition: ERF.H:1069
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > land_type_lev
Definition: ERF.H:1009
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_1
Definition: ERF.H:175
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > zflux_imask
Definition: ERF.H:1101
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > zvel_bc_data
Definition: ERF.H:862
amrex::Vector< amrex::Vector< amrex::Real > > h_sinesq_stag_ptrs
Definition: ERF.H:1420
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc_new
Definition: ERF.H:1045
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > yvel_bc_data
Definition: ERF.H:861
amrex::Vector< amrex::MultiFab > surface_state_interp
Definition: ERF.H:180
amrex::Vector< amrex::Vector< amrex::Vector< amrex::Real > > > h_rayleigh_ptrs
Definition: ERF.H:1415
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Hwave
Definition: ERF.H:1068
amrex::Vector< amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > > Tau_EB
Definition: ERF.H:1021
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > xflux_imask
Definition: ERF.H:1099
void initializeMicrophysics(const int &)
Definition: ERF.cpp:1722
void ReSize(const int &nlev)
Definition: ERF_LandSurface.H:25
Definition: ERF_EBIFTerrain.H:14
const char * buildInfoGetGitHash(int i)
amrex::Real dz0
Definition: ERF_DataStruct.H:1306
amrex::Real const_massflux_layer_lo
Definition: ERF_DataStruct.H:1430
amrex::Real const_massflux_v
Definition: ERF_DataStruct.H:1428
int massflux_klo
Definition: ERF_DataStruct.H:1432
amrex::Real grid_stretching_ratio
Definition: ERF_DataStruct.H:1304
amrex::Real const_massflux_u
Definition: ERF_DataStruct.H:1427
amrex::Real zsurf
Definition: ERF_DataStruct.H:1305
static BuildingsType buildings_type
Definition: ERF_DataStruct.H:1205
amrex::Real const_massflux_layer_hi
Definition: ERF_DataStruct.H:1431
int massflux_khi
Definition: ERF_DataStruct.H:1433
Here is the call graph for this function:

◆ ErrorEst()

void ERF::ErrorEst ( int  lev,
amrex::TagBoxArray &  tags,
amrex::Real  time,
int  ngrow 
)
override

Function to tag cells for refinement – this overrides the pure virtual function in AmrCore

Parameters
[in]levclevel of refinement at which we tag cells (0 is coarsest level)
[out]tagsarray of tagged cells
[in]timecurrent time
[in]ngrownumber of ghost cells (not used here)
26 {
27  const int clearval = TagBox::CLEAR;
28  const int tagval = TagBox::SET;
29 
30 #ifdef ERF_USE_NETCDF
31  if ((solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid)) {
32  int ratio;
33  Box subdomain;
34 
35  // This is the number of boxes that may have already been defined in the refinement_criteria_setup routine.
36  // If nb == 0 then no boxes have been specified in the inputs file, and we will use the boxes given in wrfinput_d*
37  // If nb > 0 then boxes have been specified in the inputs file, and we will use the specified boxes as long
38  // as we can ensure that they are contained inside the boxes given in wrfinput_d*
39  int nb_prespecified = num_boxes_at_level[levc+1];
40 
41  if (!nc_init_file[levc+1].empty())
42  {
43  Real levc_start_time = read_start_time_from_wrfinput(levc , nc_init_file[levc ][0]);
44  if (solverChoice.init_type == InitType::WRFInput) {
45  amrex::Print() << " WRFInput time at level " << levc << " is " << levc_start_time << std::endl;
46  } else if (solverChoice.init_type == InitType::Metgrid) {
47  amrex::Print() << " met_em time at level " << levc << " is " << levc_start_time << std::endl;
48  }
49 
50  for (int isub = 0; isub < nc_init_file[levc+1].size(); isub++) {
51  if (!have_read_nc_init_file[levc+1][isub])
52  {
53  Real levf_start_time = read_start_time_from_wrfinput(levc+1, nc_init_file[levc+1][isub]);
54  if (solverChoice.init_type == InitType::WRFInput) {
55  amrex::Print() << " WRFInput start_time at level " << levc+1 << " is " << levf_start_time << std::endl;
56  } else if (solverChoice.init_type == InitType::Metgrid) {
57  amrex::Print() << " met_em start time at level " << levc+1 << " is " << levf_start_time << std::endl;
58  }
59 
60  // We assume there is only one subdomain at levc; otherwise we don't know
61  // which one is the parent of the fine region we are trying to create
62  AMREX_ALWAYS_ASSERT(subdomains[levc].size() == 1);
63 
64  if ((solverChoice.init_type == InitType::WRFInput) && ((ref_ratio[levc][2]) != 1)) {
65  amrex::Abort("The ref_ratio specified in the inputs file must have 1 in the z direction; please use ref_ratio_vect rather than ref_ratio");
66  }
67 
68  if ( levf_start_time <= (levc_start_time + t_new[levc]) ) {
69  if (solverChoice.init_type == InitType::WRFInput) {
70  amrex::Print() << " WRFInput file to read: " << nc_init_file[levc+1][isub] << std::endl;
71  subdomain = read_subdomain_from_wrfinput(levc, nc_init_file[levc+1][isub], ratio);
72  amrex::Print() << " WRFInput subdomain " << isub << " at level " << levc+1 << " is " << subdomain << std::endl;
73  } else if (solverChoice.init_type == InitType::Metgrid) {
74  amrex::Print() << "met_em file to read: " << nc_init_file[levc+1][0] << std::endl;
75  const Box& domain = geom[levc].Domain();
76  int klo = domain.smallEnd(2);
77  int khi = domain.bigEnd(2);
78  subdomain = read_subdomain_from_metgrid(levc, nc_init_file[levc+1][0], ratio, klo, khi);
79  amrex::Print() << " met_em subdomain at level " << levc+1 << " is " << subdomain << std::endl;
80  }
81 
82  if ( (ratio != ref_ratio[levc][0]) || (ratio != ref_ratio[levc][1]) ) {
83  amrex::Print() << "File " << nc_init_file[levc+1][0] << " has refinement ratio = " << ratio << std::endl;
84  amrex::Print() << "The inputs file has refinement ratio = " << ref_ratio[levc] << std::endl;
85  amrex::Abort("These must be the same -- please edit your inputs file and try again.");
86  }
87 
88  subdomain.coarsen(ref_ratio[levc]);
89 
90  // Recall we asserted that there is only one box at level levc
91  Box coarser_level(subdomains[levc][0].minimalBox());
92  subdomain.shift(coarser_level.smallEnd());
93 
94  if (verbose > 0) {
95  amrex::Print() << " Crse version of subdomain available for tagging is" << subdomain << std::endl;
96  }
97 
98  Box new_fine(subdomain);
99  if (solverChoice.init_type == InitType::WRFInput) {
100  new_fine.refine(IntVect(ratio,ratio,1));
101  } else if (solverChoice.init_type == InitType::Metgrid) {
102  new_fine.refine(ref_ratio[levc]);
103  }
104  if (nb_prespecified == 0) {
105  num_boxes_at_level[levc+1] += 1;
106  boxes_at_level[levc+1].push_back(new_fine);
107  } else {
108  if (!new_fine.contains(boxes_at_level[levc+1][isub])) {
109  amrex::Print() << "\n";
110  amrex::Print() << "Box available in wrfinputs file " << new_fine << std::endl;
111  amrex::Print() << "Box requested for refinement in inputs file " << boxes_at_level[levc+1][isub] << std::endl;
112  amrex::Abort("Specified boxes must be contained within boxes specified in wrfinput at this level");
113  }
114  }
115 
116  Box coarsened_bx(boxes_at_level[levc+1][isub]); coarsened_bx.coarsen(ref_ratio[levc]);
117 
118  for (MFIter mfi(tags); mfi.isValid(); ++mfi)
119  {
120  auto tag_arr = tags.array(mfi); // Get device-accessible array
121 
122  Box bx = mfi.validbox() & coarsened_bx;
123 
124  if (!bx.isEmpty()) {
125  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
126  tag_arr(i,j,k) = TagBox::SET;
127  });
128  }
129  }
130  } // time is right
131  } else {
132  // Re-tag this region
133  for (MFIter mfi(tags); mfi.isValid(); ++mfi)
134  {
135  auto tag_arr = tags.array(mfi); // Get device-accessible array
136 
137  Box existing_bx_coarsened(boxes_at_level[levc+1][isub]);
138  existing_bx_coarsened.coarsen(ref_ratio[levc]);
139 
140  Box bx = mfi.validbox(); bx &= existing_bx_coarsened;
141 
142  if (!bx.isEmpty()) {
143  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
144  tag_arr(i,j,k) = TagBox::SET;
145  });
146  }
147  }
148  } // has file been read?
149  } // isub
150  return;
151  } // file not empty
152  }
153 #endif
154 
155  //
156  // Make sure the ghost cells of the level we are tagging at are filled
157  // in case we take differences that require them
158  // NOTE: We are Fillpatching only the cell-centered variables here
159  //
160  MultiFab& S_new = vars_new[levc][Vars::cons];
161  MultiFab& U_new = vars_new[levc][Vars::xvel];
162  MultiFab& V_new = vars_new[levc][Vars::yvel];
163  MultiFab& W_new = vars_new[levc][Vars::zvel];
164  //
165  if (levc == 0) {
166  FillPatchCrseLevel(levc, time, {&S_new, &U_new, &V_new, &W_new});
167  } else {
168  FillPatchFineLevel(levc, time, {&S_new, &U_new, &V_new, &W_new},
169  {&S_new, &rU_new[levc], &rV_new[levc], &rW_new[levc]},
170  base_state[levc], base_state[levc],
171  false, true);
172  }
173 
174  for (int t=0; t < ref_tags.size(); ++t)
175  {
176  //
177  // This mf must have ghost cells because we may take differences between adjacent values
178  //
179  std::unique_ptr<MultiFab> mf = std::make_unique<MultiFab>(grids[levc], dmap[levc], 1, 1);
180  mf->setVal(0.0);
181 
182  RealBox real_box = ref_tags[t].GetInfo().m_realbox;
183  if (real_box.ok()) {
184  ParmParse pp(pp_prefix); int lev_for_box; Vector<std::string> refinement_indicators;
185  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
186  std::string ref_prefix = pp_prefix + "." + refinement_indicators[t];
187  update_box_for_refinement(ref_prefix, lev_for_box, real_box, time);
188  ref_tags[t].GetInfo().SetRealBox(real_box);
189  }
190 
191  // This allows dynamic refinement based on the value of the density
192  if (ref_tags[t].Field() == "density")
193  {
194  MultiFab::Copy(*mf,vars_new[levc][Vars::cons],Rho_comp,0,1,1);
195 
196  // Refinement based on a moisture mixing ratio (qv, qc, qi, qr, qs, qg).
197  // The map from name to RhoQ component depends on the moisture model and
198  // is held in solverChoice.moisture_indices.
199  } else if ( (ref_tags[t].Field() == "qv") ||
200  (ref_tags[t].Field() == "qc") ||
201  (ref_tags[t].Field() == "qi") ||
202  (ref_tags[t].Field() == "qr") ||
203  (ref_tags[t].Field() == "qs") ||
204  (ref_tags[t].Field() == "qg") )
205  {
206  const auto& mi = solverChoice.moisture_indices;
207  int qcomp = -1;
208  if (ref_tags[t].Field() == "qv") { qcomp = mi.qv; }
209  else if (ref_tags[t].Field() == "qc") { qcomp = mi.qc; }
210  else if (ref_tags[t].Field() == "qi") { qcomp = mi.qi; }
211  else if (ref_tags[t].Field() == "qr") { qcomp = mi.qr; }
212  else if (ref_tags[t].Field() == "qs") { qcomp = mi.qs; }
213  else if (ref_tags[t].Field() == "qg") { qcomp = mi.qg; }
214  AMREX_ALWAYS_ASSERT(qcomp >= 0);
215  MultiFab::Copy( *mf, vars_new[levc][Vars::cons], qcomp, 0, 1, 1);
216  MultiFab::Divide(*mf, vars_new[levc][Vars::cons], Rho_comp, 0, 1, 1);
217 
218  // qt = total condensed water mixing ratio (qc + qi + qr + qs + qg).
219  // Excludes qv by convention here.
220  } else if (ref_tags[t].Field() == "qt") {
221  const auto& mi = solverChoice.moisture_indices;
222  const int idx_qc = mi.qc, idx_qi = mi.qi, idx_qr = mi.qr,
223  idx_qs = mi.qs, idx_qg = mi.qg;
224  AMREX_ALWAYS_ASSERT(idx_qc >= 0 || idx_qi >= 0 || idx_qr >= 0 ||
225  idx_qs >= 0 || idx_qg >= 0);
226  mf->setVal(0.0);
227  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
228  {
229  const Box& bx = mfi.growntilebox();
230  auto qt_arr = mf->array(mfi);
231  auto cons_arr = vars_new[levc][Vars::cons].const_array(mfi);
232  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
233  const Real rho_inv = Real(1.0) / cons_arr(i,j,k,Rho_comp);
234  Real q = Real(0.0);
235  if (idx_qc >= 0) { q += cons_arr(i,j,k,idx_qc) * rho_inv; }
236  if (idx_qi >= 0) { q += cons_arr(i,j,k,idx_qi) * rho_inv; }
237  if (idx_qr >= 0) { q += cons_arr(i,j,k,idx_qr) * rho_inv; }
238  if (idx_qs >= 0) { q += cons_arr(i,j,k,idx_qs) * rho_inv; }
239  if (idx_qg >= 0) { q += cons_arr(i,j,k,idx_qg) * rho_inv; }
240  qt_arr(i,j,k) = q;
241  });
242  }
243 
244  // This allows dynamic refinement based on the value of the z-component of vorticity
245  } else if (ref_tags[t].Field() == "vorticity" ) {
246  MultiFab mf_cc_vel(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(1,1,1));
247  average_face_to_cellcenter(mf_cc_vel,0,Array<const MultiFab*,3>{&U_new, &V_new, &W_new}, 1);
248 
249  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
250  {
251  const Box& bx = mfi.tilebox();
252  auto& dfab = (*mf)[mfi];
253  auto& sfab = mf_cc_vel[mfi];
254  auto& zfab = (*z_phys_cc[levc])[mfi];
255  derived::erf_dervortz(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
256  }
257 
258  // This allows dynamic refinement based on the value of the scalar/theta
259  } else if ( (ref_tags[t].Field() == "scalar" ) ||
260  (ref_tags[t].Field() == "theta" ) )
261  {
262  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
263  {
264  const Box& bx = mfi.tilebox();
265  auto& dfab = (*mf)[mfi];
266  auto& sfab = vars_new[levc][Vars::cons][mfi];
267  auto& zfab = (*z_phys_cc[levc])[mfi];
268  if (ref_tags[t].Field() == "scalar") {
269  derived::erf_derscalar(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
270  } else if (ref_tags[t].Field() == "theta") {
271  derived::erf_dertheta(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
272  }
273  } // mfi
274  // This allows dynamic refinement based on the value of updraft helicity
275  } else if (ref_tags[t].Field() == "helicity")
276  {
277  MultiFab mf_cc_vel(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(1,1,1));
278  average_face_to_cellcenter(mf_cc_vel,0,Array<const MultiFab*,3>{&U_new, &V_new, &W_new}, 1);
279 
280  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
281  {
282  const Box& bx = mfi.tilebox();
283  auto& dfab = (*mf)[mfi];
284  auto& sfab = mf_cc_vel[mfi];
285  auto& zfab = (*z_phys_cc[levc])[mfi];
286 
287  derived::erf_derhelicity(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
288  }
289  } else if (ref_tags[t].Field() == "max_reflectivity")
290  {
291  if (solverChoice.moisture_type == MoistureType::Morrison ||
292  solverChoice.moisture_type == MoistureType::SAM) {
293  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
294  {
295  const Box& bx = mfi.tilebox();
296  auto& dfab = (*mf)[mfi];
297  auto& sfab = vars_new[levc][Vars::cons][mfi];
298  auto& zfab = (*z_phys_cc[levc])[mfi];
299 
300  derived::erf_dermaxreflectivity(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
301  }
302  } else {
303  Abort("Max reflectivity is only available with Morrison and SAM microphysics.");
304  }
305  // This allows dynamic refinement based on the terrain blanking
306  } else if ( (SolverChoice::terrain_type == TerrainType::ImmersedForcing) &&
307  (ref_tags[t].Field() == "terrain_blanking") )
308  {
309  MultiFab::Copy(*mf,*terrain_blanking[levc],0,0,1,1);
310  }
311  else if (ref_tags[t].Field() == "velmag")
312  {
313  ParmParse pp(pp_prefix);
314  Vector<std::string> refinement_indicators;
315  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
316  Real velmag_threshold;
317  bool is_hurricane_tracker = false;
318  for (int i=0; i<refinement_indicators.size(); ++i)
319  {
320  if (refinement_indicators[i]=="hurricane_tracker") {
321  is_hurricane_tracker = true;
322  std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
323  ParmParse ppr(ref_prefix);
324  ppr.get("value_greater", velmag_threshold);
325  break;
326  }
327  }
328 
329  Vector<MultiFab> mf_cc_vel(1);
330  mf_cc_vel[0].define(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(0,0,0));
331  average_face_to_cellcenter(mf_cc_vel[0],0,Array<const MultiFab*,3>{&U_new, &V_new, &W_new});
332 
333  if (is_hurricane_tracker) {
334  HurricaneTracker(levc, time, mf_cc_vel[0], velmag_threshold, &tags);
335  } else {
336  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
337  {
338  const Box& bx = mfi.tilebox();
339  auto& dfab = (*mf)[mfi];
340  auto& sfab = mf_cc_vel[0][mfi];
341  auto& zfab = (*z_phys_cc[levc])[mfi];
342  derived::erf_dermagvel(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
343  }
344  }
345 
346 #ifdef ERF_USE_PARTICLES
347  } else {
348  //
349  // Particle-derived refinement. Two forms of `field_name` are
350  // supported:
351  // <species>_count : particle count per cell
352  // <species>_<mesh_var> : Eulerian mesh variable provided
353  // by the species' `meshPlotVarNames()`
354  // (e.g. `super_droplets_moisture_mass_density`,
355  // `tracer_particles_mass_density`).
356  //
357  // In both cases the field is deposited at every level in
358  // [levc, finest_level] and averaged down level-by-level so that
359  // a signal present only on a finer level still triggers
360  // refinement at the coarser level being tagged. Without this,
361  // particles localised at level 1 would not register at level 0
362  // and the fine grid would disappear at the next regrid.
363  //
364  const auto& particles_namelist( particleData.getNames() );
365  mf->setVal(0.0);
366  for (ParticlesNamesVector::size_type i = 0; i < particles_namelist.size(); i++)
367  {
368  auto* pc = particleData[particles_namelist[i]];
369  const std::string& sp_name = particles_namelist[i];
370  const std::string& field = ref_tags[t].Field();
371 
372  const std::string count_str = sp_name + "_count";
373  const std::string prefix = sp_name + "_";
374  std::string mesh_var;
375  if (field != count_str
376  && field.size() > prefix.size()
377  && field.compare(0, prefix.size(), prefix) == 0)
378  {
379  const std::string suffix = field.substr(prefix.size());
380  for (const auto& v : pc->meshPlotVarNames()) {
381  if (v == suffix) { mesh_var = v; break; }
382  }
383  }
384  if (field != count_str && mesh_var.empty()) { continue; }
385 
386  pc->resizeData();
387  const int pc_nlevs = static_cast<int>(pc->GetParticles().size());
388 
389  // Deposit at each level into per-level MultiFabs.
390  Vector<MultiFab> per_lev(finest_level+1);
391  for (int lev = levc; lev <= finest_level; lev++) {
392  per_lev[lev].define(grids[lev], dmap[lev], 1, 0);
393  per_lev[lev].setVal(0);
394  if (field == count_str) {
395  if (lev < pc_nlevs) {
396  pc->IncrementWithTotal(per_lev[lev], lev);
397  }
398  } else {
399  pc->computeMeshVar(mesh_var, per_lev[lev],
400  *z_phys_nd[lev], lev);
401  }
402  }
403 
404  // Average down level-by-level from finest to levc. This
405  // avoids multi-level coarsening (e.g. L2->L0 with ratio
406  // (4,1,4)) which can fail when fine-level boxes are not
407  // aligned to the composite refinement ratio.
408  for (int lev = finest_level; lev > levc; lev--) {
409  MultiFab temp_crse(grids[lev-1], dmap[lev-1], 1, 0);
410  temp_crse.setVal(0);
411  average_down(per_lev[lev], temp_crse,
412  0, 1, ref_ratio[lev-1]);
413  MultiFab::Add(per_lev[lev-1], temp_crse, 0, 0, 1, 0);
414  }
415 
416  MultiFab::Copy(*mf, per_lev[levc], 0, 0, 1, 0);
417  }
418 #endif
419  }
420 
421  ref_tags[t](tags,mf.get(),clearval,tagval,time,levc,geom[levc]);
422  } // loop over t
423 
424  // ********************************************************************************************
425  // Refinement based on 2d distance from the "eye" which is defined here as the (x,y) location of
426  // the integrated qv
427  // ********************************************************************************************
428  ParmParse pp(pp_prefix);
429  Vector<std::string> refinement_indicators;
430  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
431  for (int i=0; i<refinement_indicators.size(); ++i)
432  {
433  if ( (refinement_indicators[i]=="storm_tracker") && (solverChoice.moisture_type != MoistureType::None) )
434  {
435  std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
436  ParmParse ppr(ref_prefix);
437 
438  Real ref_start_time = -one;
439  ppr.query("start_time",ref_start_time);
440 
441  if (time >= ref_start_time) {
442 
443  Real max_radius = -one;
444  ppr.get("max_radius", max_radius);
445 
446  // Create the volume-weighted sum of (rho qv) in each column
447  MultiFab mf_qv_int(ba2d[levc], dmap[levc], 1, 0); mf_qv_int.setVal(0.);
448 
449  // Define the 2D MultiFab holding the column-integrated (rho qv)
450  volWgtColumnSum(levc, S_new, RhoQ1_comp, mf_qv_int, *detJ_cc[levc]);
451 
452  // Find the max value in the domain
453  IntVect eye = mf_qv_int.maxIndex(0);
454 
455  const auto dx = geom[levc].CellSizeArray();
456  const auto prob_lo = geom[levc].ProbLoArray();
457 
458  Real eye_x = prob_lo[0] + (eye[0] + myhalf) * dx[0];
459  Real eye_y = prob_lo[1] + (eye[1] + myhalf) * dx[1];
460 
461  tag_on_distance_from_eye(geom[levc], &tags, eye_x, eye_y, max_radius);
462  }
463  }
464  }
465 }
void tag_on_distance_from_eye(const Geometry &cgeom, TagBoxArray *tags, const Real eye_x, const Real eye_y, const Real rad_tag)
Definition: ERF_RefineHurricane.cpp:82
amrex::Vector< amrex::Vector< amrex::Box > > boxes_at_level
Definition: ERF.H:890
void volWgtColumnSum(int lev, const amrex::MultiFab &mf, int comp, amrex::MultiFab &mf_2d, const amrex::MultiFab &dJ)
Definition: ERF_VolWgtSum.cpp:82
void HurricaneTracker(int lev, amrex::Real time, const amrex::MultiFab &cc_vel, const amrex::Real velmag_threshold, amrex::TagBoxArray *tags=nullptr)
Definition: ERF_RefineHurricane.cpp:109
void update_box_for_refinement(std::string &ref_prefix, int &lev_for_box, amrex::RealBox &real_box, const amrex::Real time)
Definition: ERF_RefineBox.cpp:336
static amrex::Vector< amrex::Vector< std::string > > nc_init_file
Definition: ERF.H:1328
amrex::Vector< amrex::Vector< amrex::BoxArray > > subdomains
Definition: ERF.H:1454
static amrex::Vector< amrex::Vector< int > > have_read_nc_init_file
Definition: ERF.H:1329
static amrex::Vector< amrex::AMRErrorTag > ref_tags
Definition: ERF.H:1452
amrex::Vector< int > num_boxes_at_level
Definition: ERF.H:888
@ q
Definition: ERF_WSM6.H:169
@ t
Definition: ERF_WSM6.H:168
void erf_derhelicity(const Box &bx, FArrayBox &derfab, int dcomp, int, const FArrayBox &datfab, const FArrayBox &zcc_fab, const Geometry &geomdata, Real, const int *, const int)
Definition: ERF_Derive.cpp:647
void erf_dermagvel(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:480
void erf_dervortz(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &, const Geometry &geomdata, Real, const int *, const int)
Definition: ERF_Derive.cpp:415
void erf_derscalar(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:320
void erf_dertheta(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:298
void erf_dermaxreflectivity(const Box &bx, FArrayBox &derfab, int dcomp, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:566
integer, private isub
Definition: ERF_module_mp_morr_two_moment.F90:164
int qv
Definition: ERF_DataStruct.H:107
int qc
Definition: ERF_DataStruct.H:108
static InitType init_type
Definition: ERF_DataStruct.H:1196
Here is the call graph for this function:

◆ estTimeStep()

Real ERF::estTimeStep ( int  level,
long &  dt_fast_ratio 
) const

Function that calls estTimeStep for each level

Parameters
[in]levellevel of refinement (coarsest level i 0)
[out]dt_fast_ratioratio of slow to fast time step
61 {
62  BL_PROFILE("ERF::estTimeStep()");
63 
64  Real estdt_comp = bogus_large_value;
65  Real estdt_lowM = bogus_large_value;
66 
67  // We intentionally use the level 0 domain to compute whether to use this direction in the dt calculation
68  const int nxc = geom[0].Domain().length(0);
69  const int nyc = geom[0].Domain().length(1);
70 
71  auto const dxinv = geom[level].InvCellSizeArray();
72  auto const dzinv = one / dz_min[level];
73 
74  MultiFab const& S_new = vars_new[level][Vars::cons];
75 
76  MultiFab ccvel(grids[level],dmap[level],3,0);
77 
78  average_face_to_cellcenter(ccvel,0,
79  Array<const MultiFab*,3>{&vars_new[level][Vars::xvel],
80  &vars_new[level][Vars::yvel],
81  &vars_new[level][Vars::zvel]});
82 
83  bool l_substepping = (solverChoice.substepping_type[level] == SubsteppingType::Implicit);
84  int l_anelastic = solverChoice.anelastic[level];
85 
86  bool l_comp_substepping_diag = (verbose && l_substepping && !l_anelastic && solverChoice.substepping_diag);
87 
88  Real estdt_comp_inv;
89  Real estdt_vert_comp_inv;
90  Real estdt_vert_lowM_inv;
91 
92  if (l_substepping && (nxc==1) && (nyc==1)) {
93  // SCM -- should not depend on dx or dy; force minimum number of substeps
94  estdt_comp_inv = std::numeric_limits<Real>::min();
95  }
96  else if (solverChoice.terrain_type == TerrainType::EB)
97  {
98  const eb_& eb_lev = get_eb(level);
99  const MultiFab& detJ = (eb_lev.get_const_factory())->getVolFrac();
100 
101  estdt_comp_inv = ReduceMax(S_new, ccvel, detJ, 0,
102  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
103  Array4<Real const> const& s,
104  Array4<Real const> const& u,
105  Array4<Real const> const& vf) -> Real
106  {
107  Real new_comp_dt = -bogus_large_value;
108  amrex::Loop(b, [=,&new_comp_dt] (int i, int j, int k) noexcept
109  {
110  if (vf(i,j,k) > zero)
111  {
112  const Real rho = s(i, j, k, Rho_comp);
113  const Real rhotheta = s(i, j, k, RhoTheta_comp);
114 
115  // NOTE: even when moisture is present,
116  // we only use the partial pressure of the dry air
117  // to compute the soundspeed
118  Real pressure = getPgivenRTh(rhotheta);
119  Real c = std::sqrt(Gamma * pressure / rho);
120 
121  // If we are doing implicit acoustic substepping, then the z-direction does not contribute
122  // to the computation of the time step
123  if (l_substepping) {
124  if ((nxc > 1) && (nyc==1)) {
125  // 2-D in x-z
126  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]), new_comp_dt);
127  } else if ((nyc > 1) && (nxc==1)) {
128  // 2-D in y-z
129  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]), new_comp_dt);
130  } else {
131  // 3-D
132  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]),
133  ((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]), new_comp_dt);
134  }
135 
136  // If we are not doing implicit acoustic substepping, then the z-direction contributes
137  // to the computation of the time step
138  } else {
139  if (nxc > 1 && nyc > 1) {
140  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]),
141  ((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]),
142  ((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
143  } else if (nxc > 1) {
144  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]),
145  ((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
146  } else if (nyc > 1) {
147  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]),
148  ((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
149  } else {
150  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
151  }
152 
153  }
154  }
155  });
156  return new_comp_dt;
157  });
158 
159  } else {
160  const MultiFab& detJ = *detJ_cc[level];
161  estdt_comp_inv = ReduceMax(S_new, ccvel, detJ, 0,
162  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
163  Array4<Real const> const& s,
164  Array4<Real const> const& u,
165  Array4<Real const> const& dJ) -> Real
166  {
167  Real new_comp_dt = -bogus_large_value;
168  amrex::Loop(b, [=,&new_comp_dt] (int i, int j, int k) noexcept
169  {
170  {
171  const Real rho = s(i, j, k, Rho_comp);
172  const Real rhotheta = s(i, j, k, RhoTheta_comp);
173 
174  Real idz_loc = dxinv[2] / dJ(i,j,k);
175 
176  // NOTE: even when moisture is present,
177  // we only use the partial pressure of the dry air
178  // to compute the soundspeed
179  Real pressure = getPgivenRTh(rhotheta);
180  Real c = std::sqrt(Gamma * pressure / rho);
181 
182  // If we are doing implicit acoustic substepping, then the z-direction is not constrained
183  // by the speed of sound for the computation of the time step
184  if (l_substepping) {
185  if ((nxc > 1) && (nyc==1)) {
186  // 2-D in x-z
187  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]), new_comp_dt);
188  } else if ((nyc > 1) && (nxc==1)) {
189  // 2-D in y-z
190  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]), new_comp_dt);
191  } else {
192  // 3-D
193  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]),
194  ((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]),
195  ((amrex::Math::abs(u(i,j,k,2)) )*idz_loc ),new_comp_dt);
196  }
197 
198  // If we are not doing implicit acoustic substepping, then the z-direction contributes
199  // to the computation of the time step
200  } else {
201  if (nxc > 1 && nyc > 1) {
202  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]),
203  ((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]),
204  ((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
205  } else if (nxc > 1) {
206  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv[0]),
207  ((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
208  } else if (nyc > 1) {
209  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,1))+c)*dxinv[1]),
210  ((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
211  } else {
212  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,2))+c)*dzinv ), new_comp_dt);
213  }
214 
215  }
216  }
217  });
218  return new_comp_dt;
219  });
220  } // not EB
221 
222  ParallelDescriptor::ReduceRealMax(estdt_comp_inv);
223  // Globally empty level -> ReduceMax = lowest(); treat level as non-constraining.
224  estdt_comp = (estdt_comp_inv > Real(0.0)) ? (cfl / estdt_comp_inv) : bogus_large_value;
225 
226  Real estdt_lowM_inv = ReduceMax(ccvel, 0,
227  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
228  Array4<Real const> const& u) -> Real
229  {
230  Real new_lm_dt = -bogus_large_value;
231  Loop(b, [=,&new_lm_dt] (int i, int j, int k) noexcept
232  {
233  new_lm_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0)))*dxinv[0]),
234  ((amrex::Math::abs(u(i,j,k,1)))*dxinv[1]),
235  ((amrex::Math::abs(u(i,j,k,2)))*dxinv[2]), new_lm_dt);
236  });
237  return new_lm_dt;
238  });
239 
240  ParallelDescriptor::ReduceRealMax(estdt_lowM_inv);
241  if (estdt_lowM_inv > 0.0_rt)
242  estdt_lowM = cfl / estdt_lowM_inv;
243 
244  // Additional vertical diagnostics
245  if (l_comp_substepping_diag) {
246  estdt_vert_comp_inv = ReduceMax(S_new, ccvel, 0,
247  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
248  Array4<Real const> const& s,
249  Array4<Real const> const& u) -> Real
250  {
251  Real new_comp_dt = -bogus_large_value;
252  amrex::Loop(b, [=,&new_comp_dt] (int i, int j, int k) noexcept
253  {
254  {
255  const Real rho = s(i, j, k, Rho_comp);
256  const Real rhotheta = s(i, j, k, RhoTheta_comp);
257 
258  // NOTE: even when moisture is present,
259  // we only use the partial pressure of the dry air
260  // to compute the soundspeed
261  Real pressure = getPgivenRTh(rhotheta);
262  Real c = std::sqrt(Gamma * pressure / rho);
263 
264  // Look at z-direction only
265  new_comp_dt = amrex::max((amrex::Math::abs(u(i,j,k,2)) + c) * dzinv, new_comp_dt);
266  }
267  });
268  return new_comp_dt;
269  });
270 
271  estdt_vert_lowM_inv = ReduceMax(ccvel, 0,
272  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
273  Array4<Real const> const& u) -> Real
274  {
275  Real new_lowM_dt = -bogus_large_value;
276  amrex::Loop(b, [=,&new_lowM_dt] (int i, int j, int k) noexcept
277  {
278  new_lowM_dt = amrex::max((amrex::Math::abs(u(i,j,k,2))) * dzinv, new_lowM_dt);
279  });
280  return new_lowM_dt;
281  });
282 
283  ParallelDescriptor::ReduceRealMax(estdt_vert_comp_inv);
284  ParallelDescriptor::ReduceRealMax(estdt_vert_lowM_inv);
285  }
286 
287  if (verbose) {
288  if (fixed_dt[level] <= zero) {
289  Print() << "Using cfl = " << cfl << " and dx/dy/dz_min = " <<
290  one/dxinv[0] << " " << one/dxinv[1] << " " << dz_min[level] << std::endl;
291  Print() << "Compressible dt at level " << level << ": " << estdt_comp << std::endl;
292  if (estdt_lowM_inv > 0.0_rt) {
293  Print() << "Anelastic dt at level " << level << ": " << estdt_lowM << std::endl;
294  } else {
295  Print() << "Anelastic dt at level " << level << ": undefined " << std::endl;
296  }
297  }
298 
299  if (fixed_dt[level] > zero) {
300  Print() << "Based on cfl of one " << std::endl;
301  Print() << "Compressible dt at level " << level << " would be: " << estdt_comp/cfl << std::endl;
302  if (estdt_lowM_inv > 0.0_rt) {
303  Print() << "Anelastic dt at level " << level << " would be: " << estdt_lowM/cfl << std::endl;
304  } else {
305  Print() << "Anelastic dt at level " << level << " would be undefined " << std::endl;
306  }
307  Print() << "Fixed dt at level " << level << " is: " << fixed_dt[level] << std::endl;
308  if (fixed_fast_dt[level] > zero) {
309  Print() << "Fixed fast dt at level " << level << " is: " << fixed_fast_dt[level] << std::endl;
310  }
311  }
312  }
313 
314  if (solverChoice.substepping_type[level] != SubsteppingType::None) {
315  if (fixed_dt[level] > zero && fixed_fast_dt[level] > zero) {
316  dt_fast_ratio = static_cast<long>( fixed_dt[level] / fixed_fast_dt[level] );
317  if (dt_fast_ratio < 1) {
318  Abort("Invalid fixed_fast_dt: must be <= fixed_dt so mri_dt_ratio >= 1");
319  }
320  } else if (fixed_dt[level] > zero) {
321  // Max CFL_c = one for substeps by default, but we enforce a min of 4 substeps
322  auto dt_sub_max = (estdt_comp/cfl * sub_cfl);
323  dt_fast_ratio = static_cast<long>( std::max(fixed_dt[level]/dt_sub_max,Real(4.)) );
324  } else {
325  // auto dt_sub_max = (estdt_comp/cfl * sub_cfl);
326  // dt_fast_ratio = static_cast<long>( std::max(estdt_comp/dt_sub_max,Real(4.)) );
327  dt_fast_ratio = static_cast<long>( std::max(cfl / sub_cfl, Real(4.)) );
328  }
329 
330  // Force time step ratio to be an even value
332  if ( dt_fast_ratio%2 != 0) dt_fast_ratio += 1;
333  } else {
334  if ( dt_fast_ratio%6 != 0) {
335  Print() << "mri_dt_ratio = " << dt_fast_ratio
336  << " not divisible by 6 for N/3 substeps in stage 1" << std::endl;
337  dt_fast_ratio = static_cast<int>(std::ceil(dt_fast_ratio/Real(6.0)) * 6);
338  }
339  }
340 
341  if (verbose) {
342  Print() << "smallest even ratio is: " << dt_fast_ratio << std::endl;
343  }
344  } // if substepping
345 
346  // Print out some extra diagnostics -- dt calcs are repeated so as to not
347  // disrupt the overall code flow...
348  if (l_comp_substepping_diag) {
349  Real dt_diag = (fixed_dt[level] > zero) ? fixed_dt[level] : estdt_comp;
350  int ns = (fixed_mri_dt_ratio > zero) ? fixed_mri_dt_ratio : dt_fast_ratio;
351 
352  // horizontal acoustic CFL must be < 1 (fully explicit)
353  // vertical acoustic CFL may be > 1
354  Print() << "effective horiz,vert acoustic CFL with " << ns << " substeps : "
355  << (dt_diag / ns) * estdt_comp_inv << " "
356  << (dt_diag / ns) * estdt_vert_comp_inv << std::endl;
357 
358  // vertical advective CFL should be < 1, otherwise w-damping may be needed
359  Print() << "effective vert advective CFL : "
360  << dt_diag * estdt_vert_lowM_inv << std::endl;
361  }
362 
363  if (fixed_dt[level] > zero) {
364  return fixed_dt[level];
365  } else {
366  // Anelastic (substepping is not allowed)
367  if (l_anelastic) {
368 
369  // Make sure that timestep is less than the dt_max
370  estdt_lowM = amrex::min(estdt_lowM, dt_max);
371 
372  // On the first timestep enforce dt_max_initial
373  if (istep[level] == 0) {
374  return amrex::min(dt_max_initial, estdt_lowM);
375  } else {
376  return estdt_lowM;
377  }
378 
379 
380  // Compressible with or without substepping
381  } else {
382  return estdt_comp;
383  }
384  }
385 }
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:51
amrex::Vector< amrex::Real > dz_min
Definition: ERF.H:1456
amrex::Vector< amrex::Real > fixed_dt
Definition: ERF.H:1157
static amrex::Real dt_max
Definition: ERF.H:1154
amrex::Vector< amrex::Real > fixed_fast_dt
Definition: ERF.H:1158
static amrex::Real cfl
Definition: ERF.H:1149
static amrex::Real sub_cfl
Definition: ERF.H:1150
Definition: ERF_EB.H:13
@ ns
Definition: ERF_Morrison.H:47
int force_stage1_single_substep
Definition: ERF_DataStruct.H:1226
amrex::Vector< SubsteppingType > substepping_type
Definition: ERF_DataStruct.H:1228
bool substepping_diag
Definition: ERF_DataStruct.H:1236
Here is the call graph for this function:

◆ Evolve()

void ERF::Evolve ( )
138 {
139  BL_PROFILE_VAR("ERF::Evolve()", evolve);
140 
141  //
142  // cur_time = t_new is elapsed time, not total time
143  // stop_time is total time
144  // Tracked in double to avoid float32 drift over many timesteps in single-precision builds.
145  double cur_time = static_cast<double>(t_new[0]);
146 
147  // Take one coarse timestep by calling timeStep -- which recursively calls timeStep
148  // for finer levels (with or without subcycling)
149  for (int step = istep[0]; (step < max_step) && (start_time+cur_time < stop_time); ++step)
150  {
151  if (use_datetime) {
152  Print() << "\n" << getTimestamp(start_time+cur_time, datetime_format)
153  << " (" << cur_time << " s elapsed)" << std::endl;
154  }
155  Print() << "\nCoarse STEP " << step+1 << " starts ..." << std::endl;
156 
157  ComputeDt(step, cur_time);
158 
159  // Make sure we have read enough of the boundary plane data to make it through this timestep
160  if (input_bndry_planes)
161  {
162  m_r2d->read_input_files(cur_time+start_time,dt[0],m_bc_extdir_vals);
163  }
164 
165 #ifdef ERF_USE_PARTICLES
166  // We call this every time step with the knowledge that the particles may be
167  // initialized at a later time than the simulation start time.
168  // The ParticleContainer carries a "start time" so the initialization will happen
169  // only when a) time > start_time, and b) particles have not yet been initialized
170  initializeTracers((ParGDBBase*)GetParGDB(),z_phys_nd,cur_time);
171 #endif
172 
173  if(solverChoice.init_type == InitType::HindCast and
175  for(int lev=0;lev<finest_level+1;lev++){
176  WeatherDataInterpolation(lev,cur_time,z_phys_nd,false);
177  }
178  }
179 
180  if(solverChoice.init_type == InitType::HindCast and
182  for(int lev=0;lev<finest_level+1;lev++){
183  SurfaceDataInterpolation(lev,cur_time,z_phys_nd,false);
184  }
185  }
186 
187  auto dEvolveTime0 = amrex::second();
188 
189  int iteration = 1;
190  timeStep(0, cur_time, iteration);
191 
192  cur_time += static_cast<double>(dt[0]);
193  // Sync t_new[0] from accurate double to prevent float32 accumulation drift in SP builds.
194  t_new[0] = static_cast<Real>(cur_time);
195 
196  Print() << "Coarse STEP " << step+1 << " ends." << " TIME = " << cur_time
197  << " DT = " << dt[0] << std::endl;
198 
199  if (check_for_nans > 0) {
200  amrex::Print() << "Testing new state and vels for NaNs at end of timestep" << std::endl;
201  for (int lev = 0; lev <= finest_level; ++lev) {
204  }
205  }
206 
207  if (verbose > 0)
208  {
209  auto dEvolveTime = amrex::second() - dEvolveTime0;
210  ParallelDescriptor::ReduceRealMax(dEvolveTime,ParallelDescriptor::IOProcessorNumber());
211  amrex::Print() << "Timestep time = " << dEvolveTime << " seconds." << '\n';
212  }
213 
214  post_timestep(step, cur_time, dt[0]);
215 
216  WriteAtIntermediateTime(step, cur_time);
217 
218 #ifdef AMREX_MEM_PROFILING
219  {
220  std::ostringstream ss;
221  ss << "[STEP " << step+1 << "]";
222  MemProfiler::report(ss.str());
223  }
224 #endif
225 
226  if (start_time+cur_time >= stop_time - Real(1.e-6)*dt[0]) break;
227  }
228 
230 
231  BL_PROFILE_VAR_STOP(evolve);
232 }
AMREX_FORCE_INLINE std::string getTimestamp(const amrex::Real epoch_real, const std::string &datetime_format, bool add_long_frac=true)
Definition: ERF_EpochTime.H:103
void SurfaceDataInterpolation(const int nlevs, const amrex::Real time, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, bool regrid_forces_file_read)
Definition: ERF_SurfaceDataInterpolation.cpp:142
int max_step
Definition: ERF.H:1136
void WriteAtIntermediateTime(int nstep, double time)
Definition: ERF.cpp:235
amrex::Array< amrex::Array< amrex::Real, AMREX_SPACEDIM *2 >, AMREX_SPACEDIM+NBCVAR_max > m_bc_extdir_vals
Definition: ERF.H:1087
static int input_bndry_planes
Definition: ERF.H:1387
const std::string datetime_format
Definition: ERF.H:1143
bool use_datetime
Definition: ERF.H:1142
void post_timestep(int nstep, double time, amrex::Real dt_lev)
Definition: ERF.cpp:313
void ComputeDt(int step=-1, double cur_time_d=0.0)
Definition: ERF_ComputeTimestep.cpp:11
void WeatherDataInterpolation(const int nlevs, const amrex::Real time, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, bool regrid_forces_file_read)
Definition: ERF_WeatherDataInterpolation.cpp:347
std::unique_ptr< ReadBndryPlanes > m_r2d
Definition: ERF.H:1445
void timeStep(int lev, double time, int iteration)
Definition: ERF_TimeStep.cpp:18
void WriteAtFinalTime()
Definition: ERF.cpp:278
bool hindcast_lateral_forcing
Definition: ERF_DataStruct.H:1437
bool hindcast_surface_bcs
Definition: ERF_DataStruct.H:1438

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ EvolveOneStep()

amrex::Real ERF::EvolveOneStep ( amrex::Real  time,
amrex::Real  dt_request 
)
16 {
17  amrex::Real cur_time = t_new[0];
18  const int step = istep[0];
19 
20  if (start_time + cur_time >= stop_time) {
21  return amrex::Real(0.0);
22  }
23 
24  ComputeDt(step);
25 
26  int iteration = 1;
27  timeStep(0, cur_time, iteration);
28  cur_time += dt[0];
29 
30  post_timestep(step, cur_time, dt[0]);
31 
32  // ****************************************************************************************
33  // Write plotfiles at intermediate times
34  // ****************************************************************************************
35  WriteAtIntermediateTime(step, cur_time);
36 
37  return dt[0];
38 }
Here is the call graph for this function:

◆ fill_from_bndryregs()

void ERF::fill_from_bndryregs ( const amrex::Vector< amrex::MultiFab * > &  mfs,
amrex::Real  time 
)
14 {
15  //
16  // We now assume that if we read in on one face, we read in on all faces
17  //
19 
20  int lev = 0;
21  const Box& domain = geom[lev].Domain();
22 
23  const auto& dom_lo = lbound(domain);
24  const auto& dom_hi = ubound(domain);
25 
26  // Boundary-plane files are indexed by absolute simulation time.
27  Vector<std::unique_ptr<PlaneVector>>& bndry_data = m_r2d->interp_in_time(time + start_time);
28 
29  const BCRec* bc_ptr = domain_bcs_type_d.data();
30 
31  // xlo: ori = 0
32  // ylo: ori = 1
33  // zlo: ori = 2
34  // xhi: ori = 3
35  // yhi: ori = 4
36  // zhi: ori = 5
37  const auto& bdatxlo = (*bndry_data[0])[lev].const_array();
38  const auto& bdatylo = (*bndry_data[1])[lev].const_array();
39  const auto& bdatxhi = (*bndry_data[3])[lev].const_array();
40  const auto& bdatyhi = (*bndry_data[4])[lev].const_array();
41 
42  int bccomp;
43 
44  for (int var_idx = 0; var_idx < Vars::NumTypes; ++var_idx)
45  {
46  MultiFab& mf = *mfs[var_idx];
47  const int icomp = 0;
48  const int ncomp = mf.nComp();
49 
50  if (var_idx == Vars::xvel) {
51  bccomp = BCVars::xvel_bc;
52  } else if (var_idx == Vars::yvel) {
53  bccomp = BCVars::yvel_bc;
54  } else if (var_idx == Vars::zvel) {
55  bccomp = BCVars::zvel_bc;
56  } else if (var_idx == Vars::cons) {
57  bccomp = BCVars::cons_bc;
58  }
59 
60 #ifdef AMREX_USE_OMP
61 #pragma omp parallel if (Gpu::notInLaunchRegion())
62 #endif
63  for (MFIter mfi(mf); mfi.isValid(); ++mfi)
64  {
65  const Array4<Real>& dest_arr = mf.array(mfi);
66  Box bx = mfi.growntilebox();
67 
68  // x-faces
69  {
70  Box bx_xlo(bx); bx_xlo.setBig(0,dom_lo.x-1);
71  if (var_idx == Vars::xvel) bx_xlo.setBig(0,dom_lo.x);
72 
73  Box bx_xhi(bx); bx_xhi.setSmall(0,dom_hi.x+1);
74 
76  bx_xlo, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) {
77  int bc_comp = (icomp+n >= RhoScalar_comp && icomp+n < RhoScalar_comp+NSCALARS) ?
78  BCVars::RhoScalar_bc_comp : icomp+n;
79  if (bc_ptr[bc_comp].lo(0) == ERFBCType::ext_dir_ingested) {
80  int jb = std::min(std::max(j,dom_lo.y),dom_hi.y);
81  int kb = std::min(std::max(k,dom_lo.z),dom_hi.z);
82  dest_arr(i,j,k,icomp+n) = bdatxlo(dom_lo.x-1,jb,kb,bccomp+n);
83  }
84  },
85  bx_xhi, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) {
86  int bc_comp = (icomp+n >= RhoScalar_comp && icomp+n < RhoScalar_comp+NSCALARS) ?
87  BCVars::RhoScalar_bc_comp : icomp+n;
88  if (bc_ptr[bc_comp].hi(0) == ERFBCType::ext_dir_ingested) {
89  int jb = std::min(std::max(j,dom_lo.y),dom_hi.y);
90  int kb = std::min(std::max(k,dom_lo.z),dom_hi.z);
91  dest_arr(i,j,k,icomp+n) = bdatxhi(dom_hi.x+1,jb,kb,bccomp+n);
92  }
93  }
94  );
95  } // x-faces
96 
97  // y-faces
98  {
99  Box bx_ylo(bx); bx_ylo.setBig (1,dom_lo.y-1);
100  if (var_idx == Vars::yvel) bx_ylo.setBig(1,dom_lo.y);
101 
102  Box bx_yhi(bx); bx_yhi.setSmall(1,dom_hi.y+1);
103 
104  ParallelFor(
105  bx_ylo, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) {
106  int bc_comp = (icomp+n >= RhoScalar_comp && icomp+n < RhoScalar_comp+NSCALARS) ?
107  BCVars::RhoScalar_bc_comp : icomp+n;
108  if (bc_ptr[bc_comp].lo(1) == ERFBCType::ext_dir_ingested) {
109  int ib = std::min(std::max(i,dom_lo.x),dom_hi.x);
110  int kb = std::min(std::max(k,dom_lo.z),dom_hi.z);
111  dest_arr(i,j,k,icomp+n) = bdatylo(ib,dom_lo.y-1,kb,bccomp+n);
112  }
113  },
114  bx_yhi, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) {
115  int bc_comp = (icomp+n >= RhoScalar_comp && icomp+n < RhoScalar_comp+NSCALARS) ?
116  BCVars::RhoScalar_bc_comp : icomp+n;
117  if (bc_ptr[bc_comp].hi(1) == ERFBCType::ext_dir_ingested) {
118  int ib = std::min(std::max(i,dom_lo.x),dom_hi.x);
119  int kb = std::min(std::max(k,dom_lo.z),dom_hi.z);
120  dest_arr(i,j,k,icomp+n) = bdatyhi(ib,dom_hi.y+1,kb,bccomp+n);
121  }
122  }
123  );
124  } // y-faces
125  } // mf
126  } // var_idx
127 }
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:40
#define NSCALARS
Definition: ERF_IndexDefines.H:16
const auto & dom_hi
Definition: ERF_SetupVertDiff.H:2
const auto & dom_lo
Definition: ERF_SetupVertDiff.H:1
amrex::Gpu::DeviceVector< amrex::BCRec > domain_bcs_type_d
Definition: ERF.H:1081
@ RhoScalar_bc_comp
Definition: ERF_IndexDefines.H:90
@ ext_dir_ingested
Definition: ERF_IndexDefines.H:247
Here is the call graph for this function:

◆ fill_rhs()

void ERF::fill_rhs ( amrex::MultiFab &  rhs_mf,
const amrex::MultiFab &  state_mf,
amrex::Real  time,
const amrex::Geometry &  geom 
)
private

◆ FillBdyCCVels()

void ERF::FillBdyCCVels ( amrex::MultiFab &  mf_cc_vel,
amrex::Geometry &  lev_geom 
)
12 {
13  // Impose bc's at domain boundaries
14  Box domain(lev_geom.Domain());
15 
16  int ihi = domain.bigEnd(0);
17  int jhi = domain.bigEnd(1);
18  int khi = domain.bigEnd(2);
19 
20  // Impose periodicity first
21  mf_cc_vel.FillBoundary(lev_geom.periodicity());
22 
23  int jper = (lev_geom.isPeriodic(1));
24  int kper = (lev_geom.isPeriodic(2));
25 
26  for (MFIter mfi(mf_cc_vel, TilingIfNotGPU()); mfi.isValid(); ++mfi)
27  {
28  const Box& bx = mfi.tilebox();
29  const Array4<Real>& vel_arr = mf_cc_vel.array(mfi);
30 
31  if (!lev_geom.isPeriodic(0)) {
32  // Low-x side
33  if (bx.smallEnd(0) <= domain.smallEnd(0)) {
34  Real multn = ( (phys_bc_type[0] == ERF_BC::slip_wall ) ||
36  (phys_bc_type[0] == ERF_BC::symmetry ) ) ? -one : one;
37  Real multt = (phys_bc_type[0] == ERF_BC::no_slip_wall) ? -one : one;
38  Box gbx(bx); gbx.grow(1,jper); gbx.grow(2,kper);
39  ParallelFor(makeSlab(gbx,0,0), [=] AMREX_GPU_DEVICE(int , int j, int k) noexcept
40  {
41  vel_arr(-1,j,k,0) = multn*vel_arr(0,j,k,0); // u
42  vel_arr(-1,j,k,1) = multt*vel_arr(0,j,k,1); // v
43  vel_arr(-1,j,k,2) = multt*vel_arr(0,j,k,2); // w
44  });
45  }
46 
47  // High-x side
48  if (bx.bigEnd(0) >= domain.bigEnd(0)) {
49  Real multn = ( (phys_bc_type[3] == ERF_BC::slip_wall ) ||
51  (phys_bc_type[3] == ERF_BC::symmetry ) ) ? -one : one;
52  Real multt = (phys_bc_type[3] == ERF_BC::no_slip_wall) ? -one : one;
53  Box gbx(bx); gbx.grow(1,jper); gbx.grow(2,kper);
54  ParallelFor(makeSlab(gbx,0,0), [=] AMREX_GPU_DEVICE(int , int j, int k) noexcept
55  {
56  vel_arr(ihi+1,j,k,0) = multn*vel_arr(ihi,j,k,0); // u
57  vel_arr(ihi+1,j,k,1) = multt*vel_arr(ihi,j,k,1); // v
58  vel_arr(ihi+1,j,k,2) = multt*vel_arr(ihi,j,k,2); // w
59  });
60  }
61  } // !periodic
62 
63  if (!lev_geom.isPeriodic(1)) {
64  // Low-y side
65  if (bx.smallEnd(1) <= domain.smallEnd(1)) {
66  Real multn = ( (phys_bc_type[1] == ERF_BC::slip_wall ) ||
68  (phys_bc_type[1] == ERF_BC::symmetry ) ) ? -one : one;
69  Real multt = (phys_bc_type[1] == ERF_BC::no_slip_wall) ? -one : one;
70  Box gbx(bx); gbx.grow(0,1); gbx.grow(2,kper);
71  ParallelFor(makeSlab(gbx,1,0), [=] AMREX_GPU_DEVICE(int i, int , int k) noexcept
72  {
73  vel_arr(i,-1,k,0) = multt*vel_arr(i,0,k,0); // u
74  vel_arr(i,-1,k,1) = multn*vel_arr(i,0,k,1); // u
75  vel_arr(i,-1,k,2) = multt*vel_arr(i,0,k,2); // w
76  });
77  }
78 
79  // High-y side
80  if (bx.bigEnd(1) >= domain.bigEnd(1)) {
81  Real multn = ( (phys_bc_type[4] == ERF_BC::slip_wall ) ||
83  (phys_bc_type[4] == ERF_BC::symmetry ) ) ? -one : one;
84  Real multt = (phys_bc_type[4] == ERF_BC::no_slip_wall) ? -one : one;
85  Box gbx(bx); gbx.grow(0,1); gbx.grow(2,kper);
86  ParallelFor(makeSlab(gbx,1,0), [=] AMREX_GPU_DEVICE(int i, int , int k) noexcept
87  {
88  vel_arr(i,jhi+1,k,0) = multt*vel_arr(i,jhi,k,0); // u
89  vel_arr(i,jhi+1,k,1) = multn*vel_arr(i,jhi,k,1); // v
90  vel_arr(i,jhi+1,k,2) = multt*vel_arr(i,jhi,k,2); // w
91  });
92  }
93  } // !periodic
94 
95  if (!lev_geom.isPeriodic(2)) {
96  // Low-z side
97  if (bx.smallEnd(2) <= domain.smallEnd(2)) {
98  Real multn = ( (phys_bc_type[2] == ERF_BC::slip_wall ) ||
100  (phys_bc_type[2] == ERF_BC::symmetry ) ) ? -one : one;
101  Real multt = (phys_bc_type[2] == ERF_BC::no_slip_wall) ? -one : one;
102  Box gbx(bx); gbx.grow(0,1); gbx.grow(1,1);
103  ParallelFor(makeSlab(gbx,2,0), [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept
104  {
105  vel_arr(i,j,-1,0) = multt*vel_arr(i,j,0,0); // u
106  vel_arr(i,j,-1,1) = multt*vel_arr(i,j,0,1); // v
107  vel_arr(i,j,-1,2) = multn*vel_arr(i,j,0,2); // w
108  });
109  }
110 
111  // High-z side
112  if (bx.bigEnd(2) >= domain.bigEnd(2)) {
113  Real multn = ( (phys_bc_type[5] == ERF_BC::slip_wall ) ||
115  (phys_bc_type[5] == ERF_BC::symmetry ) ) ? -one : one;
116  Real multt = (phys_bc_type[5] == ERF_BC::no_slip_wall) ? -one : one;
117  Box gbx(bx); gbx.grow(0,1); gbx.grow(1,1);
118  ParallelFor(makeSlab(gbx,2,0), [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept
119  {
120  vel_arr(i,j,khi+1,0) = multt*vel_arr(i,j,khi,0); // u
121  vel_arr(i,j,khi+1,1) = multt*vel_arr(i,j,khi,1); // v
122  vel_arr(i,j,khi+1,2) = multn*vel_arr(i,j,khi,2); // w
123  });
124  }
125  } // !periodic
126  } // MFIter
127 
128  // Impose periodicity again
129  mf_cc_vel.FillBoundary(lev_geom.periodicity());
130 }
@ no_slip_wall
Here is the call graph for this function:

◆ FillCoarsePatch()

void ERF::FillCoarsePatch ( int  lev,
amrex::Real  time 
)
private
22 {
23  BL_PROFILE_VAR("FillCoarsePatch()",FillCoarsePatch);
24  AMREX_ASSERT(lev > 0);
25 
26  //
27  //****************************************************************************************************************
28  // First fill velocities and density at the COARSE level so we can convert velocity to momenta at the COARSE level
29  //****************************************************************************************************************
30  //
31  bool cons_only = false;
32  if (lev == 1) {
33  FillPatchCrseLevel(lev-1, time, {&vars_new[lev-1][Vars::cons], &vars_new[lev-1][Vars::xvel],
34  &vars_new[lev-1][Vars::yvel], &vars_new[lev-1][Vars::zvel]},
35  cons_only);
36  } else {
37  FillPatchFineLevel(lev-1, time, {&vars_new[lev-1][Vars::cons], &vars_new[lev-1][Vars::xvel],
38  &vars_new[lev-1][Vars::yvel], &vars_new[lev-1][Vars::zvel]},
39  {&vars_new[lev-1][Vars::cons],
40  &rU_new[lev-1], &rV_new[lev-1], &rW_new[lev-1]},
41  base_state[lev-1], base_state[lev-1],
42  false, cons_only);
43  }
44 
45  //
46  // ************************************************
47  // Convert velocity to momentum at the COARSE level
48  // ************************************************
49  //
50  const MultiFab* c_vfrac = nullptr;
51  if (solverChoice.terrain_type == TerrainType::EB) {
52  c_vfrac = &((get_eb(lev-1).get_const_factory())->getVolFrac());
53  }
54 
55  VelocityToMomentum(vars_new[lev-1][Vars::xvel], IntVect{0},
56  vars_new[lev-1][Vars::yvel], IntVect{0},
57  vars_new[lev-1][Vars::zvel], IntVect{0},
58  vars_new[lev-1][Vars::cons],
59  rU_new[lev-1],
60  rV_new[lev-1],
61  rW_new[lev-1],
62  Geom(lev-1).Domain(),
63  domain_bcs_type, c_vfrac);
64 
65  // Fill ghost cells of coarse momentum before interpolation to fine level.
66  // VelocityToMomentum above fills only valid cells (IntVect{0} grow). On restart
67  // from a non-AMR checkpoint, init_stuff initialises rU/rV/rW_new[lev-1] with large
68  // sentinel values for ALL cells including ghost cells; the checkpoint read then
69  // overwrites only valid cells. InterpFromCoarseLevel (see comments below) ASSUMES
70  // ghost cells at lev-1 are already filled and uses them in its stencil near periodic
71  // boundaries. Without this FillBoundary, those sentinel ghost cells contaminate the
72  // fine-level interpolation, producing unphysical velocities that blow up WENO5.
73  rU_new[lev-1].FillBoundary(geom[lev-1].periodicity());
74  rV_new[lev-1].FillBoundary(geom[lev-1].periodicity());
75  rW_new[lev-1].FillBoundary(geom[lev-1].periodicity());
76 
77  //
78  // *****************************************************************
79  // Interpolate all cell-centered variables from coarse to fine level
80  // *****************************************************************
81  //
82  Interpolater* mapper_c = &cell_cons_interp;
83  Interpolater* mapper_f = &face_cons_linear_interp;
84 
85  //
86  //************************************************************************************************
87  // Interpolate cell-centered data from coarse to fine level
88  // with InterpFromCoarseLevel which ASSUMES that all ghost cells at lev-1 have already been filled
89  // ************************************************************************************************
90  IntVect ngvect_cons = vars_new[lev][Vars::cons].nGrowVect();
91  int ncomp_cons = vars_new[lev][Vars::cons].nComp();
92 
93  InterpFromCoarseLevel(vars_new[lev ][Vars::cons], ngvect_cons, IntVect(0,0,0),
94  vars_new[lev-1][Vars::cons], 0, 0, ncomp_cons,
95  geom[lev-1], geom[lev],
96  refRatio(lev-1), mapper_c, domain_bcs_type, BCVars::cons_bc);
97 
98  // ***************************************************************************
99  // Physical bc's for cell centered variables at domain boundary
100  // ***************************************************************************
102  0,ncomp_cons,ngvect_cons,time,BCVars::cons_bc,true);
103 
104  //
105  //************************************************************************************************
106  // Interpolate x-momentum from coarse to fine level
107  // with InterpFromCoarseLevel which ASSUMES that all ghost cells at lev-1 have already been filled
108  // ************************************************************************************************
109  //
110  InterpFromCoarseLevel(rU_new[lev], IntVect{0}, IntVect{0}, rU_new[lev-1], 0, 0, 1,
111  geom[lev-1], geom[lev],
112  refRatio(lev-1), mapper_f, domain_bcs_type, BCVars::xvel_bc);
113 
114  //
115  //************************************************************************************************
116  // Interpolate y-momentum from coarse to fine level
117  // with InterpFromCoarseLevel which ASSUMES that all ghost cells at lev-1 have already been filled
118  // ************************************************************************************************
119  //
120  InterpFromCoarseLevel(rV_new[lev], IntVect{0}, IntVect{0}, rV_new[lev-1], 0, 0, 1,
121  geom[lev-1], geom[lev],
122  refRatio(lev-1), mapper_f, domain_bcs_type, BCVars::yvel_bc);
123 
124  //************************************************************************************************
125  // Interpolate z-momentum from coarse to fine level
126  // with InterpFromCoarseLevel which ASSUMES that all ghost cells at lev-1 have already been filled
127  // ************************************************************************************************
128  InterpFromCoarseLevel(rW_new[lev], IntVect{0}, IntVect{0}, rW_new[lev-1], 0, 0, 1,
129  geom[lev-1], geom[lev],
130  refRatio(lev-1), mapper_f, domain_bcs_type, BCVars::zvel_bc);
131  //
132  // *********************************************************
133  // After interpolation of momentum, convert back to velocity
134  // *********************************************************
135  //
136  for (int which_lev = lev-1; which_lev <= lev; which_lev++)
137  {
138  c_vfrac = nullptr;
139  if (solverChoice.terrain_type == TerrainType::EB) {
140  c_vfrac = &((get_eb(which_lev).get_const_factory())->getVolFrac());
141  }
142 
144  vars_new[which_lev][Vars::yvel],
145  vars_new[which_lev][Vars::zvel],
146  vars_new[which_lev][Vars::cons],
147  rU_new[which_lev],
148  rV_new[which_lev],
149  rW_new[which_lev],
150  Geom(which_lev).Domain(),
151  domain_bcs_type, c_vfrac);
152  }
153 
154  // ***************************************************************************
155  // Physical bc's at domain boundary
156  // ***************************************************************************
157  IntVect ngvect_vels = vars_new[lev][Vars::xvel].nGrowVect();
158 
160  ngvect_vels,time,BCVars::xvel_bc,true);
162  ngvect_vels,time,BCVars::yvel_bc,true);
164  ngvect_vels,time,BCVars::zvel_bc,true);
165 
166  // ***************************************************************************
167  // Since lev > 0 here we don't worry about m_r2d or wrfbdy data
168  // ***************************************************************************
169 }
void FillCoarsePatch(int lev, amrex::Real time)
Definition: ERF_FillCoarsePatch.cpp:21
Here is the call graph for this function:

◆ FillForecastStateMultiFabs()

void ERF::FillForecastStateMultiFabs ( const int  lev,
const std::string &  filename,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
amrex::Vector< amrex::Vector< amrex::MultiFab >> &  forecast_state 
)
68 {
69 
70  Vector<Real> latvec_h, lonvec_h, xvec_h, yvec_h, zvec_h;
71  Vector<Real> rho_h, uvel_h, vvel_h, wvel_h, theta_h, qv_h, qc_h, qr_h;
72 
73  ReadCustomBinaryIC(filename, latvec_h, lonvec_h,
74  xvec_h, yvec_h, zvec_h, rho_h,
75  uvel_h, vvel_h, wvel_h,
76  theta_h, qv_h, qc_h, qr_h);
77 
78  Real zmax = *std::max_element(zvec_h.begin(), zvec_h.end());
79 
80  const auto prob_lo_erf = geom[lev].ProbLoArray();
81  const auto prob_hi_erf = geom[lev].ProbHiArray();
82  const auto dx_erf = geom[lev].CellSizeArray();
83 
84  if (prob_hi_erf[2] >= zmax) {
85  Abort("ERROR: the maximum z of the domain (" + std::to_string(prob_hi_erf[2]) +
86  ") should be less than the maximum z in the forecast data (" + std::to_string(zmax) +
87  "). Change geometry.prob_hi[2] in the inputs to be less than " + std::to_string(zmax) + "."
88  );
89  }
90 
91  if(prob_lo_erf[0] < xvec_h.front() + 4*dx_erf[0]){
92  amrex::Abort("The xlo value of the domain has to be greater than " + std::to_string(xvec_h.front() + 4*dx_erf[0]));
93  }
94  if(prob_hi_erf[0] > xvec_h.back() - 4*dx_erf[0]){
95  amrex::Abort("The xhi value of the domain has to be less than " + std::to_string(xvec_h.back() - 4*dx_erf[0]));
96  }
97  if(prob_lo_erf[1] < yvec_h.front() + 4*dx_erf[1]){
98  amrex::Abort("The ylo value of the domain has to be greater than " + std::to_string(yvec_h.front() + 4*dx_erf[1]));
99  }
100  if(prob_hi_erf[1] > yvec_h.back() - 4*dx_erf[1]){
101  amrex::Abort("The yhi value of the domain has to be less than " + std::to_string(yvec_h.back() - 4*dx_erf[1]));
102  }
103 
104 
105  int nx = static_cast<int>(xvec_h.size());
106  int ny = static_cast<int>(yvec_h.size());
107  int nz = static_cast<int>(zvec_h.size());
108 
109  amrex::Real dxvec = (xvec_h[nx-1]-xvec_h[0])/(nx-1);
110  amrex::Real dyvec = (yvec_h[ny-1]-yvec_h[0])/(ny-1);
111 
112  amrex::Gpu::DeviceVector<Real> latvec_d(nx*ny), lonvec_d(nx*ny), zvec_d(nz);
113  amrex::Gpu::DeviceVector<Real> xvec_d(nx*ny*nz), yvec_d(nx*ny*nz);
114  amrex::Gpu::DeviceVector<Real> rho_d(nx*ny*nz), uvel_d(nx*ny*nz), vvel_d(nx*ny*nz), wvel_d(nx*ny*nz),
115  theta_d(nx*ny*nz), qv_d(nx*ny*nz), qc_d(nx*ny*nz), qr_d(nx*ny*nz);
116 
117  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, latvec_h.begin(), latvec_h.end(), latvec_d.begin());
118  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, lonvec_h.begin(), lonvec_h.end(), lonvec_d.begin());
119 
120  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, xvec_h.begin(), xvec_h.end(), xvec_d.begin());
121  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, yvec_h.begin(), yvec_h.end(), yvec_d.begin());
122  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, zvec_h.begin(), zvec_h.end(), zvec_d.begin());
123  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, rho_h.begin(), rho_h.end(), rho_d.begin());
124  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, theta_h.begin(), theta_h.end(), theta_d.begin());
125  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, uvel_h.begin(), uvel_h.end(), uvel_d.begin());
126  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, vvel_h.begin(), vvel_h.end(), vvel_d.begin());
127  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, wvel_h.begin(), wvel_h.end(), wvel_d.begin());
128  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, qv_h.begin(), qv_h.end(), qv_d.begin());
129  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, qc_h.begin(), qc_h.end(), qc_d.begin());
130  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, qr_h.begin(), qr_h.end(), qr_d.begin());
131 
132  amrex::Gpu::streamSynchronize();
133 
134  Real* latvec_d_ptr = latvec_d.data();
135  Real* lonvec_d_ptr = lonvec_d.data();
136  Real* xvec_d_ptr = xvec_d.data();
137  Real* yvec_d_ptr = yvec_d.data();
138  Real* zvec_d_ptr = zvec_d.data();
139  Real* rho_d_ptr = rho_d.data();
140  Real* uvel_d_ptr = uvel_d.data();
141  Real* vvel_d_ptr = vvel_d.data();
142  Real* wvel_d_ptr = wvel_d.data();
143  Real* theta_d_ptr = theta_d.data();
144  Real* qv_d_ptr = qv_d.data();
145  Real* qc_d_ptr = qc_d.data();
146  Real* qr_d_ptr = qr_d.data();
147 
148  MultiFab& erf_mf_cons = forecast_state[lev][Vars::cons];
149  MultiFab& erf_mf_xvel = forecast_state[lev][Vars::xvel];
150  MultiFab& erf_mf_yvel = forecast_state[lev][Vars::yvel];
151  MultiFab& erf_mf_zvel = forecast_state[lev][Vars::zvel];
152  MultiFab& erf_mf_latlon = forecast_state[lev][4];
153 
154  erf_mf_cons.setVal(0.0);
155  erf_mf_xvel.setVal(0.0);
156  erf_mf_yvel.setVal(0.0);
157  erf_mf_zvel.setVal(0.0);
158  erf_mf_latlon.setVal(0.0);
159 
160  // Interpolate the data on to the ERF mesh
161 
162  for (MFIter mfi(erf_mf_cons); mfi.isValid(); ++mfi) {
163  const auto z_arr = (a_z_phys_nd) ? a_z_phys_nd->const_array(mfi) :
164  Array4<const Real> {};
165  const Array4<Real> &fine_cons_arr = erf_mf_cons.array(mfi);
166  const Array4<Real> &fine_xvel_arr = erf_mf_xvel.array(mfi);
167  const Array4<Real> &fine_yvel_arr = erf_mf_yvel.array(mfi);
168  const Array4<Real> &fine_zvel_arr = erf_mf_zvel.array(mfi);
169  const Array4<Real> &fine_latlon_arr = erf_mf_latlon.array(mfi);
170 
171 
172  const Box& gbx = mfi.growntilebox(); // tilebox + ghost cells
173 
174  const Box &gtbx = mfi.tilebox(IntVect(1,0,0));
175  const Box &gtby = mfi.tilebox(IntVect(0,1,0));
176  const Box &gtbz = mfi.tilebox(IntVect(0,0,1));
177  const auto prob_lo = geom[lev].ProbLoArray();
178  const auto dx = geom[lev].CellSizeArray();
179  //const Box &gtbz = mfi.tilebox(IntVect(0,0,1));
180 
181  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
182  // Geometry (note we must include these here to get the data on device)
183  const Real x = prob_lo[0] + (i + myhalf) * dx[0];
184  const Real y = prob_lo[1] + (j + myhalf) * dx[1];
185  //const Real z = prob_lo[2] + (k + myhalf) * dx[2];
186  const Real z = (z_arr(i,j,k) + z_arr(i,j,k+1))/two;
187 
188  // First interpolate where the weather data is available from
189  Real tmp_rho, tmp_theta, tmp_qv, tmp_qc, tmp_qr, tmp_lat, tmp_lon;
190  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
191  dxvec, dyvec,
192  nx, ny, nz,
193  x, y, z,
194  rho_d_ptr, tmp_rho);
195 
196  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
197  dxvec, dyvec,
198  nx, ny, nz,
199  x, y, z,
200  theta_d_ptr, tmp_theta);
201 
202  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
203  dxvec, dyvec,
204  nx, ny, nz,
205  x, y, z,
206  qv_d_ptr, tmp_qv);
207 
208  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
209  dxvec, dyvec,
210  nx, ny, nz,
211  x, y, z,
212  qc_d_ptr, tmp_qc);
213 
214  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
215  dxvec, dyvec,
216  nx, ny, nz,
217  x, y, z,
218  qr_d_ptr, tmp_qr);
219 
220  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
221  dxvec, dyvec,
222  nx, ny, 1,
223  x, y, zero,
224  latvec_d_ptr, tmp_lat);
225 
226  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
227  dxvec, dyvec,
228  nx, ny, 1,
229  x, y, zero,
230  lonvec_d_ptr, tmp_lon);
231 
232  fine_cons_arr(i,j,k,Rho_comp) = tmp_rho;
233  fine_latlon_arr(i,j,k,0) = tmp_lat;
234  fine_latlon_arr(i,j,k,1) = tmp_lon;
235  });
236 
237  ParallelFor(gtbx, gtby, gtbz,
238  [=] AMREX_GPU_DEVICE(int i, int j, int k) {
239  // Physical location of the fine node
240  Real x = prob_lo_erf[0] + i * dx_erf[0];
241  Real y = prob_lo_erf[1] + (j+myhalf) * dx_erf[1];
242  //Real z = prob_lo_erf[2] + (k+myhalf) * dx_erf[2];
243  const Real z = (z_arr(i,j,k) + z_arr(i,j,k+1))/two;
244 
245  Real tmp_uvel;
246  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
247  dxvec, dyvec,
248  nx, ny, nz,
249  x, y, z,
250  uvel_d_ptr, tmp_uvel);
251 
252  fine_xvel_arr(i, j, k, 0) = tmp_uvel;
253  },
254  [=] AMREX_GPU_DEVICE(int i, int j, int k) {
255  // Physical location of the fine node
256  Real x = prob_lo_erf[0] + (i+myhalf) * dx_erf[0];
257  Real y = prob_lo_erf[1] + j * dx_erf[1];
258  //Real z = prob_lo_erf[2] + (k+myhalf) * dx_erf[2];
259  const Real z = (z_arr(i,j,k) + z_arr(i,j,k+1))/two;
260 
261  Real tmp_vvel;
262  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
263  dxvec, dyvec,
264  nx, ny, nz,
265  x, y, z,
266  vvel_d_ptr, tmp_vvel);
267 
268  fine_yvel_arr(i, j, k, 0) = tmp_vvel;
269  },
270  [=] AMREX_GPU_DEVICE(int i, int j, int k) {
271  // Physical location of the fine node
272  Real x = prob_lo_erf[0] + (i+myhalf) * dx_erf[0];
273  Real y = prob_lo_erf[1] + (j+myhalf) * dx_erf[1];
274  Real z = prob_lo_erf[2] + k * dx_erf[2];
275  //const Real z = (z_arr(i,j,k) + z_arr(i,j,k+1))/two;
276 
277  Real tmp_wvel;
278  bilinear_interpolation(xvec_d_ptr, yvec_d_ptr, zvec_d_ptr,
279  dxvec, dyvec,
280  nx, ny, nz,
281  x, y, z,
282  wvel_d_ptr, tmp_wvel);
283 
284  fine_zvel_arr(i, j, k, 0) = tmp_wvel;
285  });
286  }
287 
288  /*Vector<std::string> varnames = {
289  "rho", "uvel", "vvel", "wvel", "theta", "qv", "qc", "qr"
290  }; // Customize variable names
291 
292  Vector<std::string> varnames_cons = {
293  "rho", "rhotheta", "ke", "sc", "rhoqv", "rhoqc", "rhoqr"
294  }; // Customize variable names
295 
296  Vector<std::string> varnames_plot_mf = {
297  "rho", "rhotheta", "rhoqv", "rhoqc", "rhoqr", "xvel", "yvel", "zvel", "latitude", "longitude"
298  }; // Customize variable names
299 
300  const Real time = zero;
301 
302  std::string pltname = "plt_interp";
303 
304  MultiFab plot_mf(erf_mf_cons.boxArray(), erf_mf_cons.DistributionMap(),
305  10, 0);
306 
307  plot_mf.setVal(0.0);
308 
309  for (MFIter mfi(plot_mf); mfi.isValid(); ++mfi) {
310  const Array4<Real> &plot_mf_arr = plot_mf.array(mfi);
311  const Array4<Real> &erf_mf_cons_arr = erf_mf_cons.array(mfi);
312  const Array4<Real> &erf_mf_xvel_arr = erf_mf_xvel.array(mfi);
313  const Array4<Real> &erf_mf_yvel_arr = erf_mf_yvel.array(mfi);
314  const Array4<Real> &erf_mf_zvel_arr = erf_mf_zvel.array(mfi);
315  const Array4<Real> &erf_mf_latlon_arr = erf_mf_latlon.array(mfi);
316 
317  const Box& bx = mfi.validbox();
318 
319  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
320  plot_mf_arr(i,j,k,0) = erf_mf_cons_arr(i,j,k,Rho_comp);
321  plot_mf_arr(i,j,k,1) = erf_mf_cons_arr(i,j,k,RhoTheta_comp);
322  plot_mf_arr(i,j,k,2) = erf_mf_cons_arr(i,j,k,RhoQ1_comp);
323  plot_mf_arr(i,j,k,3) = erf_mf_cons_arr(i,j,k,RhoQ2_comp);
324  plot_mf_arr(i,j,k,4) = erf_mf_cons_arr(i,j,k,RhoQ3_comp);
325 
326  plot_mf_arr(i,j,k,5) = (erf_mf_xvel_arr(i,j,k,0) + erf_mf_xvel_arr(i+1,j,k,0))/two;
327  plot_mf_arr(i,j,k,6) = (erf_mf_yvel_arr(i,j,k,0) + erf_mf_yvel_arr(i,j+1,k,0))/two;
328  plot_mf_arr(i,j,k,7) = (erf_mf_zvel_arr(i,j,k,0) + erf_mf_zvel_arr(i,j,k+1,0))/two;
329 
330  plot_mf_arr(i,j,k,8) = erf_mf_latlon_arr(i,j,k,0);
331  plot_mf_arr(i,j,k,9) = erf_mf_latlon_arr(i,j,k,1);
332  });
333  }
334 
335 
336  WriteSingleLevelPlotfile(
337  pltname,
338  plot_mf,
339  varnames_plot_mf,
340  geom[0],
341  time,
342  0 // level
343  );*/
344 }
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void bilinear_interpolation(const amrex::Real *xvec, const amrex::Real *yvec, const amrex::Real *zvec, const amrex::Real dxvec, const amrex::Real dyvec, const int nx, const int ny, const int nz, const amrex::Real x, const amrex::Real y, const amrex::Real z, const amrex::Real *varvec, amrex::Real &tmp_var)
Definition: ERF_Interpolation_Bilinear.H:42
void ReadCustomBinaryIC(const std::string filename, amrex::Vector< amrex::Real > &latvec_h, amrex::Vector< amrex::Real > &lonvec_h, amrex::Vector< amrex::Real > &xvec_h, amrex::Vector< amrex::Real > &yvec_h, amrex::Vector< amrex::Real > &zvec_h, amrex::Vector< amrex::Real > &rho_h, amrex::Vector< amrex::Real > &uvel_h, amrex::Vector< amrex::Real > &vvel_h, amrex::Vector< amrex::Real > &wvel_h, amrex::Vector< amrex::Real > &theta_h, amrex::Vector< amrex::Real > &qv_h, amrex::Vector< amrex::Real > &qc_h, amrex::Vector< amrex::Real > &qr_h)
Definition: ERF_ReadCustomBinaryIC.H:15
Vector< Real > rho_h(khi+1, zero)
Gpu::DeviceVector< Real > rho_d(khi+1, zero)
Here is the call graph for this function:

◆ FillIntermediatePatch()

void ERF::FillIntermediatePatch ( int  lev,
amrex::Real  time,
const amrex::Vector< amrex::MultiFab * > &  mfs_vel,
const amrex::Vector< amrex::MultiFab * > &  mfs_mom,
int  ng_cons,
int  ng_vel,
bool  cons_only,
int  icomp_cons,
int  ncomp_cons 
)
private
33 {
34  BL_PROFILE_VAR("FillIntermediatePatch()",FillIntermediatePatch);
35  Interpolater* mapper;
36 
37  PhysBCFunctNoOp null_bc;
38 
39  //
40  // ***************************************************************************
41  // The first thing we do is interpolate the momenta on the "valid" faces of
42  // the fine grids (where the interface is coarse/fine not fine/fine) -- this
43  // will not be over-written by interpolation below because the FillPatch
44  // operators see these as valid faces. But we must have these interpolated
45  // values in the fine data before we call FillPatchTwoLevels.
46  //
47  // Also -- note that we might be filling values by interpolation at physical boundaries
48  // here but that's ok because we will overwrite those values when we impose
49  // the physical bc's below
50  // ***************************************************************************
51  if (lev>0) {
52  if (cf_set_width > 0) {
53  // We note that mfs_vel[Vars::cons] and mfs_mom[Vars::cons] are in fact the same pointer
54  FPr_c[lev-1].FillSet(*mfs_vel[Vars::cons], time, null_bc, domain_bcs_type);
55  }
56  if ( !cons_only && (cf_set_width >= 0) ) {
57  FPr_u[lev-1].FillSet(*mfs_mom[IntVars::xmom], time, null_bc, domain_bcs_type);
58  FPr_v[lev-1].FillSet(*mfs_mom[IntVars::ymom], time, null_bc, domain_bcs_type);
59  FPr_w[lev-1].FillSet(*mfs_mom[IntVars::zmom], time, null_bc, domain_bcs_type);
60  }
61  }
62 
63  // amrex::Print() << "LEVEL " << lev << " CONS ONLY " << cons_only <<
64  // " ICOMP NCOMP " << icomp_cons << " " << ncomp_cons << " NGHOST " << ng_cons << std::endl;
65 
66  if (!cons_only) {
67  AMREX_ALWAYS_ASSERT(mfs_mom.size() == IntVars::NumTypes);
68  AMREX_ALWAYS_ASSERT(mfs_vel.size() == Vars::NumTypes);
69  }
70 
71  // Enforce no penetration for thin immersed body
72  if (!cons_only) {
73  // Enforce no penetration for thin immersed body
74  if (xflux_imask[lev]) {
75  ApplyMask(*mfs_mom[IntVars::xmom], *xflux_imask[lev]);
76  }
77  if (yflux_imask[lev]) {
78  ApplyMask(*mfs_mom[IntVars::ymom], *yflux_imask[lev]);
79  }
80  if (zflux_imask[lev]) {
81  ApplyMask(*mfs_mom[IntVars::zmom], *zflux_imask[lev]);
82  }
83  }
84 
85  //
86  // We now start working on conserved quantities + VELOCITY
87  //
88  if (lev == 0)
89  {
90  // We don't do anything here because we will call the physbcs routines below,
91  // which calls FillBoundary and fills other domain boundary conditions
92  // Physical boundaries will be filled below
93 
94  if (!cons_only)
95  {
96  // ***************************************************************************
97  // We always come in to this call with updated momenta but we need to create updated velocity
98  // in order to impose the rest of the bc's
99  // ***************************************************************************
100  const MultiFab* c_vfrac = nullptr;
101  if (solverChoice.terrain_type == TerrainType::EB) {
102  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
103  }
104 
105  // This only fills VALID region of velocity
106  MomentumToVelocity(*mfs_vel[Vars::xvel], *mfs_vel[Vars::yvel], *mfs_vel[Vars::zvel],
107  *mfs_vel[Vars::cons],
108  *mfs_mom[IntVars::xmom], *mfs_mom[IntVars::ymom], *mfs_mom[IntVars::zmom],
109  Geom(lev).Domain(), domain_bcs_type, c_vfrac);
110  }
111  }
112  else
113  {
114  //
115  // We must fill a temporary then copy it back so we don't double add/subtract
116  //
117  MultiFab mf(mfs_vel[Vars::cons]->boxArray(),mfs_vel[Vars::cons]->DistributionMap(),
118  mfs_vel[Vars::cons]->nComp() ,mfs_vel[Vars::cons]->nGrowVect());
119  //
120  // Set all components to bogus_large_value, then copy just the density from *mfs_vel[Vars::cons]
121  //
122  mf.setVal(bogus_large_value);
123  MultiFab::Copy(mf,*mfs_vel[Vars::cons],Rho_comp,Rho_comp,1,mf.nGrowVect());
124 
125  Vector<MultiFab*> fmf = {mfs_vel[Vars::cons],mfs_vel[Vars::cons]};
126  Vector<MultiFab*> cmf = {&vars_old[lev-1][Vars::cons], &vars_new[lev-1][Vars::cons]};
127  Vector<Real> ctime = {t_old[lev-1], t_new[lev-1]};
128  Vector<Real> ftime = {time,time};
129 
130  if (interpolation_type == StateInterpType::Perturbational)
131  {
132  if (icomp_cons+ncomp_cons > 1)
133  {
134  // Divide (rho theta) by rho to get theta
135  MultiFab::Divide(*mfs_vel[Vars::cons],*mfs_vel[Vars::cons],Rho_comp,RhoTheta_comp,1,IntVect{0});
136 
137  // Subtract theta_0 from theta
138  MultiFab::Subtract(*mfs_vel[Vars::cons],base_state[lev],BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
139 
140  if (!amrex::almostEqual(time,ctime[1])) {
141  MultiFab::Divide(vars_old[lev-1][Vars::cons], vars_old[lev-1][Vars::cons],
142  Rho_comp,RhoTheta_comp,1,vars_old[lev-1][Vars::cons].nGrowVect());
143  MultiFab::Subtract(vars_old[lev-1][Vars::cons], base_state[lev-1],
144  BaseState::th0_comp,RhoTheta_comp,1,vars_old[lev-1][Vars::cons].nGrowVect());
145  }
146  if (!amrex::almostEqual(time,ctime[0])) {
147  MultiFab::Divide(vars_new[lev-1][Vars::cons], vars_new[lev-1][Vars::cons],
148  Rho_comp,RhoTheta_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
149  MultiFab::Subtract(vars_new[lev-1][Vars::cons], base_state[lev-1],
150  BaseState::th0_comp,RhoTheta_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
151  }
152  }
153 
154  // Subtract rho_0 from rho before we interpolate -- note we only subtract
155  // on valid region of mf since the ghost cells will be filled below
156  if (icomp_cons == 0)
157  {
158  MultiFab::Subtract(*mfs_vel[Vars::cons],base_state[lev],BaseState::r0_comp,Rho_comp,1,IntVect{0});
159 
160  if (!amrex::almostEqual(time,ctime[1])) {
161  MultiFab::Subtract(vars_old[lev-1][Vars::cons], base_state[lev-1],
162  BaseState::r0_comp,Rho_comp,1,vars_old[lev-1][Vars::cons].nGrowVect());
163  }
164  if (!amrex::almostEqual(time,ctime[0])) {
165  MultiFab::Subtract(vars_new[lev-1][Vars::cons], base_state[lev-1],
166  BaseState::r0_comp,Rho_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
167  }
168  }
169  } // interpolation_type == StateInterpType::Perturbational
170 
171  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
172  mapper = &cell_cons_interp;
173  FillPatchTwoLevels(mf, IntVect{ng_cons}, IntVect(0,0,0),
174  time, cmf, ctime, fmf, ftime,
175  icomp_cons, icomp_cons, ncomp_cons, geom[lev-1], geom[lev],
176  refRatio(lev-1), mapper, domain_bcs_type,
177  icomp_cons);
178 
179  if (interpolation_type == StateInterpType::Perturbational)
180  {
181  if (icomp_cons == 0)
182  {
183  // Restore the coarse values to what they were
184  if (!amrex::almostEqual(time,ctime[1])) {
185  MultiFab::Add(vars_old[lev-1][Vars::cons], base_state[lev-1],
186  BaseState::r0_comp,Rho_comp,1,vars_old[lev-1][Vars::cons].nGrowVect());
187  }
188  if (!amrex::almostEqual(time,ctime[0])) {
189  MultiFab::Add(vars_new[lev-1][Vars::cons], base_state[lev-1],
190  BaseState::r0_comp,Rho_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
191  }
192 
193  // Set values in the cells outside the domain boundary so that we can do the Add
194  // without worrying about uninitialized values outside the domain -- these
195  // will be filled in the physbcs call
196  mf.setDomainBndry(bogus_large_value,Rho_comp,1,geom[lev]);
197 
198  // Add rho_0 back to rho after we interpolate -- on all the valid + ghost region
199  MultiFab::Add(mf, base_state[lev],BaseState::r0_comp,Rho_comp,1,IntVect{ng_cons});
200  }
201 
202  if (icomp_cons+ncomp_cons > 1)
203  {
204  // Add theta_0 to theta
205  if (!amrex::almostEqual(time,ctime[1])) {
206  MultiFab::Add(vars_old[lev-1][Vars::cons], base_state[lev-1],
207  BaseState::th0_comp,RhoTheta_comp,1,vars_old[lev-1][Vars::cons].nGrowVect());
208  MultiFab::Multiply(vars_old[lev-1][Vars::cons], vars_old[lev-1][Vars::cons],
209  Rho_comp,RhoTheta_comp,1,vars_old[lev-1][Vars::cons].nGrowVect());
210  }
211  if (!amrex::almostEqual(time,ctime[0])) {
212  MultiFab::Add(vars_new[lev-1][Vars::cons], base_state[lev-1],
213  BaseState::th0_comp,RhoTheta_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
214  MultiFab::Multiply(vars_new[lev-1][Vars::cons], vars_new[lev-1][Vars::cons],
215  Rho_comp,RhoTheta_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
216  }
217 
218  // Multiply theta by rho to get (rho theta)
219  MultiFab::Multiply(*mfs_vel[Vars::cons],*mfs_vel[Vars::cons],Rho_comp,RhoTheta_comp,1,IntVect{0});
220 
221  // Add theta_0 to theta
222  MultiFab::Add(*mfs_vel[Vars::cons],base_state[lev],BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
223 
224  // Add theta_0 back to theta
225  MultiFab::Add(mf,base_state[lev],BaseState::th0_comp,RhoTheta_comp,1,IntVect{ng_cons});
226 
227  // Multiply (theta) by rho to get (rho theta)
228  MultiFab::Multiply(mf,mf,Rho_comp,RhoTheta_comp,1,IntVect{ng_cons});
229  }
230  } // interpolation_type == StateInterpType::Perturbational
231 
232  // Impose physical bc's on fine data (note time and 0 are not used)
233  // Note that we do this after the FillPatch because imposing physical bc's on fine ghost
234  // cells that need to be filled from coarse requires that we have done the interpolation first
235  bool do_fb = true; bool do_terrain_adjustment = false;
236  (*physbcs_cons[lev])(mf,*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
237  icomp_cons,ncomp_cons,IntVect{ng_cons},time,BCVars::cons_bc,
238  do_fb, do_terrain_adjustment);
239 
240  // Make sure to only copy back the components we worked on
241  MultiFab::Copy(*mfs_vel[Vars::cons],mf,icomp_cons,icomp_cons,ncomp_cons,IntVect{ng_cons});
242 
243  // *****************************************************************************************
244 
245  if (!cons_only)
246  {
247  // ***************************************************************************
248  // We always come in to this call with updated momenta but we need to create updated velocity
249  // in order to impose the rest of the bc's
250  // ***************************************************************************
251  const MultiFab* c_vfrac = nullptr;
252  if (solverChoice.terrain_type == TerrainType::EB) {
253  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
254  }
255 
256  // This only fills VALID region of velocity
257  MomentumToVelocity(*mfs_vel[Vars::xvel], *mfs_vel[Vars::yvel], *mfs_vel[Vars::zvel],
258  *mfs_vel[Vars::cons],
259  *mfs_mom[IntVars::xmom], *mfs_mom[IntVars::ymom], *mfs_mom[IntVars::zmom],
260  Geom(lev).Domain(), domain_bcs_type, c_vfrac);
261 
262  mapper = &face_cons_linear_interp;
263 
264  //
265  // NOTE: All interpolation here happens on velocities not momenta;
266  // note we only do the interpolation and FillBoundary here,
267  // physical bc's are imposed later
268  //
269  // NOTE: This will only fill velocity from coarse grid *outside* the fine grids
270  // unlike the FillSet calls above which filled momenta on the coarse/fine bdy
271  //
272 
273  MultiFab& mfu = *mfs_vel[Vars::xvel];
274 
275  fmf = {&mfu,&mfu};
276  cmf = {&vars_old[lev-1][Vars::xvel], &vars_new[lev-1][Vars::xvel]};
277 
278  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
279  FillPatchTwoLevels(mfu, IntVect{ng_vel}, IntVect(0,0,0),
280  time, cmf, ctime, fmf, ftime,
281  0, 0, 1, geom[lev-1], geom[lev],
282  refRatio(lev-1), mapper, domain_bcs_type,
284 
285  // *****************************************************************************************
286 
287  MultiFab& mfv = *mfs_vel[Vars::yvel];
288 
289  fmf = {&mfv,&mfv};
290  cmf = {&vars_old[lev-1][Vars::yvel], &vars_new[lev-1][Vars::yvel]};
291 
292  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
293  FillPatchTwoLevels(mfv, IntVect{ng_vel}, IntVect(0,0,0),
294  time, cmf, ctime, fmf, ftime,
295  0, 0, 1, geom[lev-1], geom[lev],
296  refRatio(lev-1), mapper, domain_bcs_type,
298 
299  // *****************************************************************************************
300 
301  MultiFab& mfw = *mfs_vel[Vars::zvel];
302 
303  fmf = {&mfw,&mfw};
304  cmf = {&vars_old[lev-1][Vars::zvel], &vars_new[lev-1][Vars::zvel]};
305 
306  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
307  FillPatchTwoLevels(mfw, IntVect{ng_vel}, IntVect(0,0,0),
308  time, cmf, ctime, fmf, ftime,
309  0, 0, 1, geom[lev-1], geom[lev],
310  refRatio(lev-1), mapper, domain_bcs_type,
312  } // !cons_only
313  } // lev > 0
314 
315  // ***************************************************************************
316  // Physical bc's at domain boundary
317  // ***************************************************************************
318  IntVect ngvect_cons = IntVect(ng_cons,ng_cons,ng_cons);
319  IntVect ngvect_vels = IntVect(ng_vel ,ng_vel ,ng_vel);
320 
321  bool do_fb = true;
322 
323 #ifdef ERF_USE_NETCDF
324  if (solverChoice.use_real_bcs && (lev==0)) {
325  fill_from_realbdy(mfs_vel,time,cons_only,icomp_cons,ncomp_cons,ngvect_cons,ngvect_vels);
326  do_fb = false;
327  }
328 #endif
329 
330  if (m_r2d && !solverChoice.use_real_bcs) { fill_from_bndryregs(mfs_vel,time); }
331 
332  // We call this even if use_real_bcs is true because these will fill the vertical bcs
333  (*physbcs_cons[lev])(*mfs_vel[Vars::cons],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
334  icomp_cons,ncomp_cons,ngvect_cons,time,BCVars::cons_bc, do_fb);
335  if (!cons_only) {
336  (*physbcs_u[lev])(*mfs_vel[Vars::xvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
337  ngvect_vels,time,BCVars::xvel_bc, do_fb);
338  (*physbcs_v[lev])(*mfs_vel[Vars::yvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
339  ngvect_vels,time,BCVars::yvel_bc, do_fb);
340  (*physbcs_w[lev])(*mfs_vel[Vars::zvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
341  ngvect_vels,time,BCVars::zvel_bc, do_fb);
342  }
343  // ***************************************************************************
344 
345  // We always come in to this call with momenta so we need to leave with momenta!
346  // We need to make sure we convert back on all ghost cells/faces because this is
347  // how velocity from fine-fine copies (as well as physical and interpolated bcs) will be filled
348  if (!cons_only)
349  {
350  IntVect ngu = (!solverChoice.use_num_diff) ? IntVect(1,1,1) : mfs_vel[Vars::xvel]->nGrowVect();
351  IntVect ngv = (!solverChoice.use_num_diff) ? IntVect(1,1,1) : mfs_vel[Vars::yvel]->nGrowVect();
352  IntVect ngw = (!solverChoice.use_num_diff) ? IntVect(1,1,0) : mfs_vel[Vars::zvel]->nGrowVect();
353 
354  const MultiFab* c_vfrac = nullptr;
355  if (solverChoice.terrain_type == TerrainType::EB) {
356  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
357  }
358 
359  VelocityToMomentum(*mfs_vel[Vars::xvel], ngu,
360  *mfs_vel[Vars::yvel], ngv,
361  *mfs_vel[Vars::zvel], ngw,
362  *mfs_vel[Vars::cons],
363  *mfs_mom[IntVars::xmom], *mfs_mom[IntVars::ymom], *mfs_mom[IntVars::zmom],
364  Geom(lev).Domain(),
365  domain_bcs_type, c_vfrac);
366  }
367 
368  // NOTE: There are not FillBoundary calls here for the following reasons:
369  // Removal of the FillBoundary (FB) calls has bee completed for the following reasons:
370  //
371  // one physbc_cons is called before VelocityToMomentum and a FB is completed in that functor.
372  // Therefore, the conserved CC vars have their inter-rank ghost cells filled and then their
373  // domain ghost cells filled from the BC operations. We should not call FB on this MF again.
374  //
375  // two physbc_u/v/w is also called before VelocityToMomentum and a FB is completed those functors.
376  // Furthermore, VelocityToMomentum operates on a growntilebox so we exit that routine with momentum
377  // filled everywhere---i.e., physbc_u/v/w fills velocity ghost cells (inter-rank and domain)
378  // and then V2M does the conversion to momenta everywhere; so there is again no need to do a FB on momenta.
379 }
AMREX_GPU_HOST AMREX_FORCE_INLINE void ApplyMask(amrex::MultiFab &dst, const amrex::iMultiFab &imask, const int nghost=0)
Definition: ERF_Utils.H:441
void fill_from_bndryregs(const amrex::Vector< amrex::MultiFab * > &mfs, amrex::Real time)
Definition: ERF_BoundaryConditionsBndryReg.cpp:13
void FillIntermediatePatch(int lev, amrex::Real time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, const amrex::Vector< amrex::MultiFab * > &mfs_mom, int ng_cons, int ng_vel, bool cons_only, int icomp_cons, int ncomp_cons)
Definition: ERF_FillIntermediatePatch.cpp:28
@ NumTypes
Definition: ERF_IndexDefines.H:196
Here is the call graph for this function:

◆ FillPatchCrseLevel()

void ERF::FillPatchCrseLevel ( int  lev,
double  time,
const amrex::Vector< amrex::MultiFab * > &  mfs_vel,
bool  cons_only = false 
)
private
293 {
294  BL_PROFILE_VAR("ERF::FillPatchCrseLevel()",ERF_FillPatchCrseLevel);
295 
296  AMREX_ALWAYS_ASSERT(lev == 0);
297 
298  Real time = static_cast<Real>(time_d);
299 
300  IntVect ngvect_cons = mfs_vel[Vars::cons]->nGrowVect();
301  IntVect ngvect_vels = mfs_vel[Vars::xvel]->nGrowVect();
302 
303  Vector<Real> ftime = {t_old[lev], t_new[lev]};
304 
305  //
306  // Below we call FillPatchSingleLevel which does NOT fill ghost cells outside the domain
307  //
308 
309  Vector<MultiFab*> fmf;
310  Vector<MultiFab*> fmf_u;
311  Vector<MultiFab*> fmf_v;
312  Vector<MultiFab*> fmf_w;
313 
314  if (amrex::almostEqual(time,ftime[0])) {
315  fmf = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::cons]};
316  } else if (amrex::almostEqual(time,ftime[1])) {
317  fmf = {&vars_new[lev][Vars::cons], &vars_new[lev][Vars::cons]};
318  } else {
319  fmf = {&vars_old[lev][Vars::cons], &vars_new[lev][Vars::cons]};
320  }
321 
322  const int ncomp = mfs_vel[Vars::cons]->nComp();
323 
324  FillPatchSingleLevel(*mfs_vel[Vars::cons], ngvect_cons, time, fmf, IntVect(0,0,0), ftime,
325  0, 0, ncomp, geom[lev]);
326 
327  if (!cons_only) {
328  if (amrex::almostEqual(time,ftime[0])) {
329  fmf_u = {&vars_old[lev][Vars::xvel], &vars_old[lev][Vars::xvel]};
330  fmf_v = {&vars_old[lev][Vars::yvel], &vars_old[lev][Vars::yvel]};
331  fmf_w = {&vars_old[lev][Vars::zvel], &vars_old[lev][Vars::zvel]};
332  } else if (amrex::almostEqual(time,ftime[1])) {
333  fmf_u = {&vars_new[lev][Vars::xvel], &vars_new[lev][Vars::xvel]};
334  fmf_v = {&vars_new[lev][Vars::yvel], &vars_new[lev][Vars::yvel]};
335  fmf_w = {&vars_new[lev][Vars::zvel], &vars_new[lev][Vars::zvel]};
336  } else {
337  fmf_u = {&vars_old[lev][Vars::xvel], &vars_new[lev][Vars::xvel]};
338  fmf_v = {&vars_old[lev][Vars::yvel], &vars_new[lev][Vars::yvel]};
339  fmf_w = {&vars_old[lev][Vars::zvel], &vars_new[lev][Vars::zvel]};
340  }
341  FillPatchSingleLevel(*mfs_vel[Vars::xvel], ngvect_vels, time, fmf_u,
342  IntVect(0,0,0), ftime, 0, 0, 1, geom[lev]);
343 
344  FillPatchSingleLevel(*mfs_vel[Vars::yvel], ngvect_vels, time, fmf_v,
345  IntVect(0,0,0), ftime, 0, 0, 1, geom[lev]);
346 
347  FillPatchSingleLevel(*mfs_vel[Vars::zvel], ngvect_vels, time, fmf_w,
348  IntVect(0,0,0), ftime, 0, 0, 1, geom[lev]);
349  } // !cons_only
350 
351  // ***************************************************************************
352  // Physical bc's at domain boundary
353  // ***************************************************************************
354  int icomp_cons = 0;
355  int ncomp_cons = mfs_vel[Vars::cons]->nComp();
356 
357  bool do_fb = true;
358 
359 #ifdef ERF_USE_NETCDF
360  if(solverChoice.use_real_bcs && (lev==0)) {
361  fill_from_realbdy(mfs_vel,time,cons_only,icomp_cons,ncomp_cons,ngvect_cons,ngvect_vels);
362  do_fb = false;
363  }
364 #endif
365 
366  if (m_r2d && !solverChoice.use_real_bcs) { fill_from_bndryregs(mfs_vel,time); }
367 
368  // We call this even if use_real_bcs is true because these will fill the vertical bcs
369  // Note that we call FillBoundary inside the physbcs call
370  (*physbcs_cons[lev])(*mfs_vel[Vars::cons],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
371  icomp_cons,ncomp_cons,ngvect_cons,time,BCVars::cons_bc, do_fb);
372  if (!cons_only) {
373  (*physbcs_u[lev])(*mfs_vel[Vars::xvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
374  ngvect_vels,time,BCVars::xvel_bc, do_fb);
375  (*physbcs_v[lev])(*mfs_vel[Vars::yvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
376  ngvect_vels,time,BCVars::yvel_bc, do_fb);
377  (*physbcs_w[lev])(*mfs_vel[Vars::zvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
378  ngvect_vels,time,BCVars::zvel_bc, do_fb);
379  }
380 }
Here is the call graph for this function:

◆ FillPatchFineLevel()

void ERF::FillPatchFineLevel ( int  lev,
double  time,
const amrex::Vector< amrex::MultiFab * > &  mfs_vel,
const amrex::Vector< amrex::MultiFab * > &  mfs_mom,
const amrex::MultiFab &  old_base_state,
const amrex::MultiFab &  new_base_state,
bool  fillset = true,
bool  cons_only = false 
)
private
26 {
27  BL_PROFILE_VAR("ERF::FillPatchFineLevel()",ERF_FillPatchFineLevel);
28 
29  AMREX_ALWAYS_ASSERT(lev > 0);
30 
31  Real time = static_cast<Real>(time_d);
32 
33  Interpolater* mapper = nullptr;
34 
35  PhysBCFunctNoOp null_bc;
36 
37  //
38  // ***************************************************************************
39  // The first thing we do is interpolate the momenta on the "valid" faces of
40  // the fine grids (where the interface is coarse/fine not fine/fine) -- this
41  // will not be over-written below because the FillPatch operators see these as
42  // valid faces.
43  //
44  // Note that we interpolate momentum not velocity, but all the other boundary
45  // conditions are imposed on velocity, so we convert to momentum here then
46  // convert back.
47  // ***************************************************************************
48  if (fillset) {
49  if (cf_set_width > 0) {
50  FPr_c[lev-1].FillSet(*mfs_vel[Vars::cons], time, null_bc, domain_bcs_type);
51  }
52  if (cf_set_width >= 0 && !cons_only) {
53 
54  const MultiFab* c_vfrac = nullptr;
55  if (solverChoice.terrain_type == TerrainType::EB) {
56  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
57  }
58 
59  VelocityToMomentum(*mfs_vel[Vars::xvel], IntVect{0},
60  *mfs_vel[Vars::yvel], IntVect{0},
61  *mfs_vel[Vars::zvel], IntVect{0},
62  *mfs_vel[Vars::cons],
63  *mfs_mom[IntVars::xmom],
64  *mfs_mom[IntVars::ymom],
65  *mfs_mom[IntVars::zmom],
66  Geom(lev).Domain(),
67  domain_bcs_type, c_vfrac);
68 
69  FPr_u[lev-1].FillSet(*mfs_mom[IntVars::xmom], time, null_bc, domain_bcs_type);
70  FPr_v[lev-1].FillSet(*mfs_mom[IntVars::ymom], time, null_bc, domain_bcs_type);
71  FPr_w[lev-1].FillSet(*mfs_mom[IntVars::zmom], time, null_bc, domain_bcs_type);
72 
73  MomentumToVelocity(*mfs_vel[Vars::xvel], *mfs_vel[Vars::yvel], *mfs_vel[Vars::zvel],
74  *mfs_vel[Vars::cons],
75  *mfs_mom[IntVars::xmom],
76  *mfs_mom[IntVars::ymom],
77  *mfs_mom[IntVars::zmom],
78  Geom(lev).Domain(),
79  domain_bcs_type, c_vfrac);
80  }
81  }
82 
83  IntVect ngvect_cons = mfs_vel[Vars::cons]->nGrowVect();
84  IntVect ngvect_vels = mfs_vel[Vars::xvel]->nGrowVect();
85 
86  Vector<Real> ftime = {t_old[lev ], t_new[lev ]};
87  Vector<Real> ctime = {t_old[lev-1], t_new[lev-1]};
88 
89  amrex::Real small_dt = Real(1.e-8) * (ftime[1] - ftime[0]);
90 
91  Vector<MultiFab*> fmf;
92  if ( amrex::almostEqual(time,ftime[0]) || (time-ftime[0]) < small_dt ) {
93  fmf = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::cons]};
94  } else if (amrex::almostEqual(time,ftime[1])) {
95  fmf = {&vars_new[lev][Vars::cons], &vars_new[lev][Vars::cons]};
96  } else {
97  fmf = {&vars_old[lev][Vars::cons], &vars_new[lev][Vars::cons]};
98  }
99  Vector<MultiFab*> cmf = {&vars_old[lev-1][Vars::cons], &vars_new[lev-1][Vars::cons]};
100 
101  // We must fill a temporary then copy it back so we don't double add/subtract
102  MultiFab mf_c(mfs_vel[Vars::cons]->boxArray(),mfs_vel[Vars::cons]->DistributionMap(),
103  mfs_vel[Vars::cons]->nComp() ,mfs_vel[Vars::cons]->nGrowVect());
104 
105  mapper = &cell_cons_interp;
106 
107  if (interpolation_type == StateInterpType::Perturbational)
108  {
109  // Divide (rho theta) by rho to get theta (before we subtract rho0 from rho!)
110  if (!amrex::almostEqual(time,ctime[1])) {
111  MultiFab::Divide(vars_old[lev-1][Vars::cons],vars_old[lev-1][Vars::cons],
112  Rho_comp,RhoTheta_comp,1,ngvect_cons);
113  MultiFab::Subtract(vars_old[lev-1][Vars::cons],base_state[lev-1],
114  BaseState::r0_comp,Rho_comp,1,ngvect_cons);
115  MultiFab::Subtract(vars_old[lev-1][Vars::cons],base_state[lev-1],
116  BaseState::th0_comp,RhoTheta_comp,1,ngvect_cons);
117  }
118  if (!amrex::almostEqual(time,ctime[0])) {
119  MultiFab::Divide(vars_new[lev-1][Vars::cons],vars_new[lev-1][Vars::cons],
120  Rho_comp,RhoTheta_comp,1,ngvect_cons);
121  MultiFab::Subtract(vars_new[lev-1][Vars::cons],base_state[lev-1],
122  BaseState::r0_comp,Rho_comp,1,ngvect_cons);
123  MultiFab::Subtract(vars_new[lev-1][Vars::cons],base_state[lev-1],
124  BaseState::th0_comp,RhoTheta_comp,1,ngvect_cons);
125  }
126 
127  if (!amrex::almostEqual(time,ftime[1])) {
128  MultiFab::Divide(vars_old[lev ][Vars::cons],vars_old[lev ][Vars::cons],
129  Rho_comp,RhoTheta_comp,1,IntVect{0});
130  MultiFab::Subtract(vars_old[lev ][Vars::cons],old_base_state,
131  BaseState::r0_comp,Rho_comp,1,IntVect{0});
132  MultiFab::Subtract(vars_old[lev ][Vars::cons],old_base_state,
133  BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
134  }
135  if (!amrex::almostEqual(time,ftime[0])) {
136  MultiFab::Divide(vars_new[lev ][Vars::cons],vars_new[lev ][Vars::cons],
137  Rho_comp,RhoTheta_comp,1,IntVect{0});
138  MultiFab::Subtract(vars_new[lev ][Vars::cons],old_base_state,
139  BaseState::r0_comp,Rho_comp,1,IntVect{0});
140  MultiFab::Subtract(vars_new[lev ][Vars::cons],old_base_state,
141  BaseState::th0_comp,RhoTheta_comp,1,IntVect{0});
142  }
143  }
144 
145  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
146  FillPatchTwoLevels(mf_c, ngvect_cons, IntVect(0,0,0),
147  time, cmf, ctime, fmf, ftime,
148  0, 0, mf_c.nComp(), geom[lev-1], geom[lev],
149  refRatio(lev-1), mapper, domain_bcs_type,
151 
152  if (interpolation_type == StateInterpType::Perturbational)
153  {
154  // Restore the coarse values to what they were
155  if (!amrex::almostEqual(time,ctime[1])) {
156  MultiFab::Add(vars_old[lev-1][Vars::cons], base_state[lev-1],
157  BaseState::r0_comp,Rho_comp,1,ngvect_cons);
158  MultiFab::Add(vars_old[lev-1][Vars::cons], base_state[lev-1],
159  BaseState::th0_comp,RhoTheta_comp,1,ngvect_cons);
160  MultiFab::Multiply(vars_old[lev-1][Vars::cons], vars_old[lev-1][Vars::cons],
161  Rho_comp,RhoTheta_comp,1,ngvect_cons);
162  }
163  if (!amrex::almostEqual(time,ctime[0])) {
164  MultiFab::Add(vars_new[lev-1][Vars::cons], base_state[lev-1],
165  BaseState::r0_comp,Rho_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
166  MultiFab::Add(vars_new[lev-1][Vars::cons], base_state[lev-1],
167  BaseState::th0_comp,RhoTheta_comp,1,vars_new[lev-1][Vars::cons].nGrowVect());
168  MultiFab::Multiply(vars_new[lev-1][Vars::cons], vars_new[lev-1][Vars::cons],
169  Rho_comp,RhoTheta_comp,1,ngvect_cons);
170  }
171 
172  if (!amrex::almostEqual(time,ftime[1])) {
173  MultiFab::Add(vars_old[lev][Vars::cons],base_state[lev ],BaseState::r0_comp,Rho_comp,1,ngvect_cons);
174  MultiFab::Add(vars_old[lev][Vars::cons],base_state[lev ],BaseState::th0_comp,RhoTheta_comp,1,ngvect_cons);
175  MultiFab::Multiply(vars_old[lev][Vars::cons], vars_old[lev][Vars::cons],
176  Rho_comp,RhoTheta_comp,1,ngvect_cons);
177  }
178  if (!amrex::almostEqual(time,ftime[0])) {
179  MultiFab::Add(vars_new[lev][Vars::cons], base_state[lev],BaseState::r0_comp,Rho_comp,1,ngvect_cons);
180  MultiFab::Add(vars_new[lev][Vars::cons], base_state[lev],BaseState::th0_comp,RhoTheta_comp,1,ngvect_cons);
181  MultiFab::Multiply(vars_new[lev][Vars::cons], vars_new[lev][Vars::cons],
182  Rho_comp,RhoTheta_comp,1,ngvect_cons);
183  }
184 
185  // Set values in the cells outside the domain boundary so that we can do the Add
186  // without worrying about uninitialized values outside the domain -- these
187  // will be filled in the physbcs call
188  mf_c.setDomainBndry(bogus_large_value,0,2,geom[lev]); // Do both rho and (rho theta) together
189 
190  // Add rho_0 back to rho and theta_0 back to theta
191  MultiFab::Add(mf_c, new_base_state,BaseState::r0_comp,Rho_comp,1,ngvect_cons);
192  MultiFab::Add(mf_c, new_base_state,BaseState::th0_comp,RhoTheta_comp,1,ngvect_cons);
193 
194  // Multiply (theta) by rho to get (rho theta)
195  MultiFab::Multiply(mf_c,mf_c,Rho_comp,RhoTheta_comp,1,ngvect_cons);
196  }
197 
198  MultiFab::Copy(*mfs_vel[Vars::cons],mf_c,0,0,mf_c.nComp(),mf_c.nGrowVect());
199 
200  // ***************************************************************************************
201 
202  if (!cons_only)
203  {
204  mapper = &face_cons_linear_interp;
205 
206  MultiFab& mf_u = *mfs_vel[Vars::xvel];
207  MultiFab& mf_v = *mfs_vel[Vars::yvel];
208  MultiFab& mf_w = *mfs_vel[Vars::zvel];
209 
210  Vector<MultiFab*> fmf_u; Vector<MultiFab*> fmf_v; Vector<MultiFab*> fmf_w;
211  Vector<MultiFab*> cmf_u; Vector<MultiFab*> cmf_v; Vector<MultiFab*> cmf_w;
212 
213  // **********************************************************************
214 
215  if ( amrex::almostEqual(time,ftime[0]) || (time-ftime[0]) < small_dt ) {
216  fmf_u = {&vars_old[lev][Vars::xvel], &vars_old[lev][Vars::xvel]};
217  fmf_v = {&vars_old[lev][Vars::yvel], &vars_old[lev][Vars::yvel]};
218  fmf_w = {&vars_old[lev][Vars::zvel], &vars_old[lev][Vars::zvel]};
219  } else if ( amrex::almostEqual(time,ftime[1]) ) {
220  fmf_u = {&vars_new[lev][Vars::xvel], &vars_new[lev][Vars::xvel]};
221  fmf_v = {&vars_new[lev][Vars::yvel], &vars_new[lev][Vars::yvel]};
222  fmf_w = {&vars_new[lev][Vars::zvel], &vars_new[lev][Vars::zvel]};
223  } else {
224  fmf_u = {&vars_old[lev][Vars::xvel], &vars_new[lev][Vars::xvel]};
225  fmf_v = {&vars_old[lev][Vars::yvel], &vars_new[lev][Vars::yvel]};
226  fmf_w = {&vars_old[lev][Vars::zvel], &vars_new[lev][Vars::zvel]};
227  }
228  cmf_u = {&vars_old[lev-1][Vars::xvel], &vars_new[lev-1][Vars::xvel]};
229  cmf_v = {&vars_old[lev-1][Vars::yvel], &vars_new[lev-1][Vars::yvel]};
230  cmf_w = {&vars_old[lev-1][Vars::zvel], &vars_new[lev-1][Vars::zvel]};
231 
232  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
233  FillPatchTwoLevels(mf_u, ngvect_vels, IntVect(0,0,0),
234  time, cmf_u, ctime, fmf_u, ftime,
235  0, 0, 1, geom[lev-1], geom[lev],
236  refRatio(lev-1), mapper, domain_bcs_type,
238 
239  FillPatchTwoLevels(mf_v, ngvect_vels, IntVect(0,0,0),
240  time, cmf_v, ctime, fmf_v, ftime,
241  0, 0, 1, geom[lev-1], geom[lev],
242  refRatio(lev-1), mapper, domain_bcs_type,
244 
245  // We put these here because these may be used in constructing omega outside the
246  // domain when fillpatching w
247  bool do_fb = true;
248  (*physbcs_u[lev])(*mfs_vel[Vars::xvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
249  ngvect_vels,time,BCVars::xvel_bc, do_fb);
250  (*physbcs_v[lev])(*mfs_vel[Vars::yvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
251  ngvect_vels,time,BCVars::yvel_bc, do_fb);
252 
253  // **********************************************************************
254 
255  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
256  FillPatchTwoLevels(mf_w, ngvect_vels, IntVect(0,0,0),
257  time, cmf_w, ctime, fmf_w, ftime,
258  0, 0, 1, geom[lev-1], geom[lev],
259  refRatio(lev-1), mapper, domain_bcs_type,
261  } // !cons_only
262 
263  // ***************************************************************************
264  // Physical bc's at domain boundary
265  // ***************************************************************************
266  int icomp_cons = 0;
267  int ncomp_cons = mfs_vel[Vars::cons]->nComp();
268 
269  bool do_fb = true;
270 
271  if (m_r2d && !solverChoice.use_real_bcs) fill_from_bndryregs(mfs_vel,time);
272 
273  // We call this even if use_real_bcs is true because these will fill the vertical bcs
274  // Note that we call FillBoundary inside the physbcs call
275  (*physbcs_cons[lev])(*mfs_vel[Vars::cons],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
276  icomp_cons,ncomp_cons,ngvect_cons,time,BCVars::cons_bc, do_fb);
277  if (!cons_only) {
278  // Note that we need to fill u and v in the case of terrain because we will use
279  // these in the call of WFromOmega in lateral ghost cells of the fine grid
280  // (*physbcs_u[lev])(*mfs_vel[Vars::xvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
281  // ngvect_vels,time,BCVars::xvel_bc, do_fb);
282  // (*physbcs_v[lev])(*mfs_vel[Vars::yvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
283  // ngvect_vels,time,BCVars::yvel_bc, do_fb);
284  (*physbcs_w[lev])(*mfs_vel[Vars::zvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
285  ngvect_vels,time,BCVars::zvel_bc, do_fb);
286  }
287 }
Here is the call graph for this function:

◆ FillSurfaceStateMultiFabs()

void ERF::FillSurfaceStateMultiFabs ( const int  lev,
const std::string &  filename,
amrex::Vector< amrex::MultiFab > &  surface_state 
)
22 {
23  // Open the binary file in input mode
24  std::ifstream infile(filename, std::ios::binary);
25  if (!infile) {
26  std::cerr << "Error: Could not open file " << filename << std::endl;
27  }
28  Vector<Real> xvec_h, yvec_h, zvec_h;
29  Vector<Real> sst_h, q_star_h, t_star_h, u_star_h, ls_mask_h;
30 
31  int nx, ny, nz, ndata;
32  float value;
33 
34  // Read the four integers
35  infile.read(reinterpret_cast<char*>(&nx), sizeof(int));
36  infile.read(reinterpret_cast<char*>(&ny), sizeof(int));
37  infile.read(reinterpret_cast<char*>(&nz), sizeof(int));
38  infile.read(reinterpret_cast<char*>(&ndata), sizeof(int));
39 
40  amrex::Gpu::DeviceVector<Real> xvec_d(nx*ny*nz), yvec_d(nx*ny*nz), zvec_d(nz);
41  for(int i=0; i<nx; i++) {
42  infile.read(reinterpret_cast<char*>(&value), sizeof(float));
43  xvec_h.emplace_back(value);
44  }
45  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, xvec_h.begin(), xvec_h.end(), xvec_d.begin());
46 
47  for(int j=0; j<ny; j++) {
48  infile.read(reinterpret_cast<char*>(&value), sizeof(float));
49  yvec_h.emplace_back(value);
50  }
51  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, yvec_h.begin(), yvec_h.end(), yvec_d.begin());
52 
53  for(int k=0; k<nz; k++) {
54  infile.read(reinterpret_cast<char*>(&value), sizeof(float));
55  zvec_h.emplace_back(value);
56  }
57  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, zvec_h.begin(), zvec_h.end(), zvec_d.begin());
58 
59  // Vector to store the data
60 
61  Vector<Real>* data_h = nullptr; // Declare pointer outside the loop
62 
63  Real* xvec_d_ptr = xvec_d.data();
64  Real* yvec_d_ptr = yvec_d.data();
65 
66  Real dxvec = (xvec_h[nx-1]-xvec_h[0])/(nx-1);
67  Real dyvec = (yvec_h[ny-1]-yvec_h[0])/(ny-1);
68 
69  // Read the file
70  for(int idx=0; idx<ndata; idx++){
71  if(idx == 0){
72  data_h = &sst_h;
73  } else if (idx==1) {
74  data_h = &q_star_h;
75  } else if (idx==2) {
76  data_h = &t_star_h;
77  } else if (idx==3) {
78  data_h = &u_star_h;
79  } else if(idx==4) {
80  data_h = &ls_mask_h;
81  }
82  for(int k=0; k<nz; k++) {
83  for(int j=0; j<ny; j++) {
84  for(int i=0; i<nx; i++) {
85  infile.read(reinterpret_cast<char*>(&value), sizeof(float));
86  //if(idx == 3) {
87  //printf("theta is %0.15g, %0.15g, %0.15g %0.15g\n", xvec_h[i], yvec_h[j], zvec_h[k], value);
88  //}
89  data_h->emplace_back(value);
90  }
91  }
92  }
93  }
94 
95  infile.close();
96 
97  amrex::Gpu::DeviceVector<Real> ls_mask_d(nx*ny*nz), sst_d(nx*ny*nz);
98 
99  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, ls_mask_h.begin(), ls_mask_h.end(), ls_mask_d.begin());
100  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, sst_h.begin(), sst_h.end(), sst_d.begin());
101 
102  Real* ls_mask_d_ptr = ls_mask_d.data();
103  Real* sst_d_ptr = sst_d.data();
104 
105  const auto prob_lo = geom[lev].ProbLo();
106  const auto dx = geom[lev].CellSize();
107 
108  for (amrex::MFIter mfi(surface_state[lev]); mfi.isValid(); ++mfi) {
109  const Box gbx = mfi.growntilebox();
110  const Array4<Real>& surf_arr = surface_state[lev].array(mfi);
111 
112  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
113 
114  if(k == 0) {
115  const Real x = prob_lo[0] + (i + myhalf) * dx[0];
116  const Real y = prob_lo[1] + (j + myhalf) * dx[1];
117 
118  // First interpolate where the weather data is available from
119  Real tmp_ls_mask, tmp_sst;
120 
121  bilinear_interpolation_2d(xvec_d_ptr, yvec_d_ptr,
122  dxvec, dyvec,
123  nx, ny,
124  x, y,
125  ls_mask_d_ptr, tmp_ls_mask);
126 
127  bilinear_interpolation_2d(xvec_d_ptr, yvec_d_ptr,
128  dxvec, dyvec,
129  nx, ny,
130  x, y,
131  sst_d_ptr, tmp_sst);
132 
133  surf_arr(i, j, 0) = std::min(tmp_ls_mask, amrex::Real(1.0));
134  surf_arr(i, j, 1) = tmp_sst;
135  }
136  });
137  }
138 
139 }
amrex::Real value
Definition: ERF_HurricaneDiagnostics.H:20
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:287
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void bilinear_interpolation_2d(const amrex::Real *xvec, const amrex::Real *yvec, const amrex::Real dxvec, const amrex::Real dyvec, const int nx, const int ny, amrex::Real x, amrex::Real y, const amrex::Real *varvec, amrex::Real &tmp_var)
Definition: ERF_Interpolation_Bilinear.H:156
Here is the call graph for this function:

◆ FindInitialEye()

bool ERF::FindInitialEye ( int  lev,
const amrex::MultiFab &  cc_vel,
const amrex::Real  velmag_threshold,
amrex::Real eye_x,
amrex::Real eye_y 
)
15 {
16  const auto dx = geom[levc].CellSizeArray();
17  const auto prob_lo = geom[levc].ProbLoArray();
18 
19  Gpu::DeviceVector<Real> d_coords(2, zero);
20  Gpu::DeviceVector<int> d_found(1,0);
21 
22  Real* d_coords_ptr = d_coords.data();
23  int* d_found_ptr = d_found.data();
24 
25  for (MFIter mfi(mf_cc_vel); mfi.isValid(); ++mfi)
26  {
27  const Box& box = mfi.validbox();
28  const Array4<const Real>& vel_arr = mf_cc_vel.const_array(mfi);
29 
30  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k)
31  {
32  Real magnitude = std::sqrt(vel_arr(i,j,k,0) * vel_arr(i,j,k,0) +
33  vel_arr(i,j,k,1) * vel_arr(i,j,k,1) +
34  vel_arr(i,j,k,2) * vel_arr(i,j,k,2));
35 
36  magnitude *= Real(3.6);
37 
38  Real z = prob_lo[2] + (k + myhalf) * dx[2];
39 
40  // Check if magnitude exceeds threshold
41  if (z < Real(2000.) && magnitude > velmag_threshold) {
42  // Use atomic operations to set found flag and store coordinates
43  Gpu::Atomic::Add(&d_found_ptr[0], 1); // Mark as found
44 
45  Real x = prob_lo[0] + (i + myhalf) * dx[0];
46  Real y = prob_lo[1] + (j + myhalf) * dx[1];
47 
48  // Store coordinates
49  Gpu::Atomic::Add(&d_coords_ptr[0],x); // Store x index
50  Gpu::Atomic::Add(&d_coords_ptr[1],y); // Store x index
51  }
52  });
53  }
54 
55  // Synchronize to ensure all threads complete their execution
56  amrex::Gpu::streamSynchronize(); // Wait for all GPU threads to finish
57 
58  Vector<int> h_found(1,0);
59  Gpu::copy(Gpu::deviceToHost, d_found.begin(), d_found.end(), h_found.begin());
60  ParallelAllReduce::Sum(h_found.data(), h_found.size(), ParallelContext::CommunicatorAll());
61 
62  // Broadcast coordinates if found
63  if (h_found[0] > 0) {
64  Vector<Real> h_coords(2,-bogus_large_value);
65  Gpu::copy(Gpu::deviceToHost, d_coords.begin(), d_coords.end(), h_coords.begin());
66 
67  ParallelAllReduce::Sum(h_coords.data(), h_coords.size(), ParallelContext::CommunicatorAll());
68 
69  eye_x = h_coords[0]/h_found[0];
70  eye_y = h_coords[1]/h_found[0];
71 
72  } else {
73  // Random large negative numbers so we don't trigger refinement in this case
74  eye_x = -bogus_large_value;
75  eye_y = -bogus_large_value;
76  }
77 
78  return (h_found[0] > 0);
79 }
Here is the call graph for this function:

◆ get_eb()

eb_ const& ERF::get_eb ( int  lev) const
inlineprivatenoexcept
1733  {
1734  AMREX_ASSERT(lev >= 0 && lev < eb.size() && eb[lev] != nullptr);
1735  return *eb[lev];
1736  }

◆ getAdvFluxReg()

AMREX_FORCE_INLINE amrex::YAFluxRegister* ERF::getAdvFluxReg ( int  lev)
inlineprivate
1519  {
1520  return advflux_reg[lev];
1521  }

◆ getCPUTime()

static amrex::Real ERF::getCPUTime ( )
inlinestaticprivate
1611  {
1612  int numCores = amrex::ParallelDescriptor::NProcs();
1613 #ifdef _OPENMP
1614  numCores = numCores * omp_get_max_threads();
1615 #endif
1616 
1617  amrex::Real T =
1618  numCores * amrex::Real(amrex::ParallelDescriptor::second() - startCPUTime) +
1620 
1621  return T;
1622  }
Real T
Definition: ERF_InitCustomPert_Bubble.H:105
static amrex::Real previousCPUTimeUsed
Definition: ERF.H:1607
static amrex::Real startCPUTime
Definition: ERF.H:1606

◆ GotoNextLine()

void ERF::GotoNextLine ( std::istream &  is)
staticprivate

Utility to skip to next line in Header file input stream.

18 {
19  constexpr std::streamsize bl_ignore_max { 100000 };
20  is.ignore(bl_ignore_max, '\n');
21 }

◆ HurricaneEyeTracker_WRF()

void ERF::HurricaneEyeTracker_WRF ( const SolverChoice solverChoice)
369 {
370  static bool is_start = true;
371  int levc=finest_level;
372 
373  const MoistureType moisture_type = sc.moisture_type;
374  const Real hurricane_eye_latitude = sc.hurricane_eye_latitude;
375  const Real hurricane_eye_longitude = sc.hurricane_eye_longitude;
376 
377  if(is_start and restart_chkfile.empty()){
379  vars_new[levc],
380  hurricane_eye_latitude,
381  hurricane_eye_longitude);
382  is_start = false;
383  } else {
384  if(!restart_chkfile.empty()) {
386  }
388  moisture_type);
389  }
391 }
void HurricaneEyeTrackerInitial_WRF(const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, const amrex::Real &hurricane_eye_latitude, const amrex::Real &hurricane_eye_longitude)
Definition: ERF_HurricaneDiagnostics_WRF.cpp:157
void HurricaneEyeTrackerNotInitial_WRF(const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, MoistureType moisture_type)
Definition: ERF_HurricaneDiagnostics_WRF.cpp:204
std::string restart_chkfile
Definition: ERF.H:1146
void ReadStormTrackerRestart()
Definition: ERF_HurricaneDiagnostics_WRF.cpp:268
void HurricaneTrackerCircle_WRF()
Definition: ERF_HurricaneDiagnostics_WRF.cpp:131

◆ HurricaneEyeTrackerInitial_WRF()

void ERF::HurricaneEyeTrackerInitial_WRF ( const amrex::Geometry &  geom,
const amrex::Vector< amrex::MultiFab > &  S_data,
const amrex::Real hurricane_eye_latitude,
const amrex::Real hurricane_eye_longitude 
)
161 {
162  int levc = finest_level;
163  Gpu::DeviceScalar<Real> d_val_min(1e10);
164  Gpu::DeviceScalar<int> d_i_min(-1), d_j_min(-1);
165 
166  Real* d_val_min_ptr = d_val_min.dataPtr();
167  int* d_i_min_ptr = d_i_min.dataPtr();
168  int* d_j_min_ptr = d_j_min.dataPtr();
169 
170  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
171  const Box& box = mfi.validbox();
172  FArrayBox& fab_lat = (*(lat_m[levc]))[mfi];
173  FArrayBox& fab_lon = (*(lon_m[levc]))[mfi];
174  const Array4<Real>& lat_arr = fab_lat.array();
175  const Array4<Real>& lon_arr = fab_lon.array();
176 
177  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
178  if (k==0) {
179 
180  Real dlat = lat_arr(i,j,0) - hurricane_eye_latitude;
181  Real dlon = lon_arr(i,j,0) - hurricane_eye_longitude;
182  Real dist = std::sqrt(dlat*dlat + dlon*dlon);
183  // Atomic min using device pointer from DeviceVector
184  Real old = Gpu::Atomic::Min(&d_val_min_ptr[0], dist);
185  //Gpu::Atomic::Min(&d_val_min_ptr[0], dist);
186  if (dist < old) {
187  // We are the new minimum; record indices
188  d_i_min_ptr[0] = i;
189  d_j_min_ptr[0] = j;
190  }
191  }
192  });
193  }
194 
195  Real global_val_min;
196  int global_i_min, global_j_min;
197 
198  ComputeGlobalMinLocation_WRF(lev_geom, S_data,
199  d_val_min_ptr, d_i_min_ptr, d_j_min_ptr,
200  global_val_min, global_i_min, global_j_min);
201 }
void ComputeGlobalMinLocation_WRF(const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, amrex::Real *d_val_min_ptr, int *d_i_min_ptr, int *d_j_min_ptr, amrex::Real &global_val_min, int &global_i_min, int &global_j_min)
Definition: ERF_HurricaneDiagnostics_WRF.cpp:36
Here is the call graph for this function:

◆ HurricaneEyeTrackerNotInitial_WRF()

void ERF::HurricaneEyeTrackerNotInitial_WRF ( const amrex::Geometry &  geom,
const amrex::Vector< amrex::MultiFab > &  S_data,
MoistureType  moisture_type 
)
207 {
208 
209  if (hurricane_eye_track_xy.empty()) {
210  Print() << "Error: hurricane_eye_track_xy is empty!\n";
211  Abort("Attempted to access hurricane_eye_track_xy[0]");
212  }
213 
214  Real tmp_x_eye = hurricane_eye_track_xy.back()[0];
215  Real tmp_y_eye = hurricane_eye_track_xy.back()[1];
216 
217  if(ParallelDescriptor::IOProcessor()){
218  std::cout << "The value of x y are " << tmp_x_eye << " " << tmp_y_eye << std::endl;
219  }
220 
221  Gpu::DeviceScalar<Real> d_val_min(1e10);
222  Gpu::DeviceScalar<int> d_i_min(-1), d_j_min(-1);
223 
224  Real* d_val_min_ptr = d_val_min.dataPtr();
225  int* d_i_min_ptr = d_i_min.dataPtr();
226  int* d_j_min_ptr = d_j_min.dataPtr();
227 
228  bool use_moisture = (moisture_type != MoistureType::None);
229  const int ncomp = S_data[IntVars::cons].nComp();
230 
231  const auto dx = lev_geom.CellSizeArray();
232  const auto prob_lo = lev_geom.ProbLoArray();
233 
234  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
235  const Box& box = mfi.validbox();
236  const Array4<Real const>& S_arr = S_data[IntVars::cons].const_array(mfi);
237 
238  ParallelFor(box,[=] AMREX_GPU_DEVICE(int i, int j, int k) {
239  if(k==0) {
240  Real x = prob_lo[0] + (i+myhalf)*dx[0];
241  Real y = prob_lo[1] + (j+myhalf)*dx[1];
242  Real dist = std::sqrt((x-tmp_x_eye)*(x-tmp_x_eye) + (y-tmp_y_eye)*(y-tmp_y_eye));
243  if(dist < 200e3) {
244  Real qv_for_p = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : 0;
245  const Real rhotheta = S_arr(i,j,k,RhoTheta_comp);
246  Real pressure = getPgivenRTh(rhotheta,qv_for_p);
247  Real old = Gpu::Atomic::Min(&d_val_min_ptr[0], pressure);
248  //Gpu::Atomic::Min(&d_val_min_ptr[0], dist);
249  if (old > pressure) {
250  // We are the new minimum; record indices
251  d_i_min_ptr[0] = i;
252  d_j_min_ptr[0] = j;
253  }
254  }
255  }
256  });
257  }
258 
259  Real global_val_min;
260  int global_i_min, global_j_min;
261 
262  ComputeGlobalMinLocation_WRF (lev_geom, S_data,
263  d_val_min_ptr, d_i_min_ptr, d_j_min_ptr,
264  global_val_min, global_i_min, global_j_min);
265 }
Here is the call graph for this function:

◆ HurricaneTracker()

void ERF::HurricaneTracker ( int  lev,
amrex::Real  time,
const amrex::MultiFab &  cc_vel,
const amrex::Real  velmag_threshold,
amrex::TagBoxArray *  tags = nullptr 
)
114 {
115  bool is_found;
116 
117  Real eye_x, eye_y;
118 
119  if (time==zero || hurricane_eye_track_xy.empty()) {
120  is_found = FindInitialEye(levc, mf_cc_vel, velmag_threshold, eye_x, eye_y);
121  } else {
122  is_found = true;
123  const auto& last = hurricane_eye_track_xy.back();
124  eye_x = last[0];
125  eye_y = last[1];
126  }
127 
128  if (is_found) {
129  const int exponent = max_level-1-levc;
130  Real rad_tag = std::ldexp(Real(4.e5), exponent);
131  tag_on_distance_from_eye(geom[levc], tags, eye_x, eye_y, rad_tag);
132  }
133 }
void tag_on_distance_from_eye(const Geometry &cgeom, TagBoxArray *tags, const Real eye_x, const Real eye_y, const Real rad_tag)
Definition: ERF_RefineHurricane.cpp:82
bool FindInitialEye(int lev, const amrex::MultiFab &cc_vel, const amrex::Real velmag_threshold, amrex::Real &eye_x, amrex::Real &eye_y)
Definition: ERF_RefineHurricane.cpp:11
Here is the call graph for this function:

◆ HurricaneTrackerCircle_WRF()

void ERF::HurricaneTrackerCircle_WRF ( )
132 {
133  // Check that there is at least one eye position
134  if (hurricane_eye_track_xy.empty()) return;
135 
136  // Get the last known (x, y) position of the eye
137  const auto [x_last, y_last] = hurricane_eye_track_xy.back();
138 
139  // Define circle properties
140  const int n_points = 100; // number of points on the circle
141  const Real radius = 200e3; // radius in meters (example: 50 km)
142 
143  // Clear previous points and reserve space
144  hurricane_tracker_circle.clear();
145  hurricane_tracker_circle.reserve(n_points);
146 
147  // Fill the circle points
148  for (int i = 0; i < n_points; ++i) {
149  Real theta = two * static_cast<Real>(M_PI) * static_cast<Real>(i) / static_cast<Real>(n_points);
150  Real x = x_last + radius * std::cos(theta);
151  Real y = y_last + radius * std::sin(theta);
152  hurricane_tracker_circle.push_back({x, y});
153  }
154 }
#define M_PI
Definition: ERF_HurricaneDiagnostics_WRF.cpp:26
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_tracker_circle
Definition: ERF.H:173

◆ ImposeBCsOnPhi()

void ERF::ImposeBCsOnPhi ( int  lev,
amrex::MultiFab &  phi,
const amrex::Box &  subdomain 
)

Impose bc's on the pressure that comes out of the solve

13 {
14  BL_PROFILE("ERF::ImposeBCsOnPhi()");
15 
16  auto const sub_lo = lbound(subdomain);
17  auto const sub_hi = ubound(subdomain);
18 
19  auto const dom_lo = lbound(geom[lev].Domain());
20  auto const dom_hi = ubound(geom[lev].Domain());
21 
22  phi.setBndry(Real(1.e25));
23  phi.FillBoundary(geom[lev].periodicity());
24 
25  // ****************************************************************************
26  // Impose bc's on pprime
27  // ****************************************************************************
28 #ifdef _OPENMP
29 #pragma omp parallel if (Gpu::notInLaunchRegion())
30 #endif
31  for (MFIter mfi(phi,TilingIfNotGPU()); mfi.isValid(); ++mfi)
32  {
33  Array4<Real> const& pp_arr = phi.array(mfi);
34  Box const& bx = mfi.tilebox();
35  auto const bx_lo = lbound(bx);
36  auto const bx_hi = ubound(bx);
37 
38  auto bc_type_xlo = domain_bc_type[Orientation(0,Orientation::low)];
39  auto bc_type_xhi = domain_bc_type[Orientation(0,Orientation::high)];
40  auto bc_type_ylo = domain_bc_type[Orientation(1,Orientation::low)];
41  auto bc_type_yhi = domain_bc_type[Orientation(1,Orientation::high)];
42  auto bc_type_zhi = domain_bc_type[Orientation(2,Orientation::high)];
43 
44  if ( (bx_lo.x == dom_lo.x) && (bc_type_xlo == "Outflow" || bc_type_xlo == "Open") && !solverChoice.use_real_bcs) {
45  ParallelFor(makeSlab(bx,0,dom_lo.x), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
46  {
47  pp_arr(i-1,j,k) = -pp_arr(i,j,k);
48  });
49  } else if (bx_lo.x == sub_lo.x) {
50  ParallelFor(makeSlab(bx,0,sub_lo.x), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
51  {
52  pp_arr(i-1,j,k) = pp_arr(i,j,k);
53  });
54  }
55 
56  if ( (bx_hi.x == dom_hi.x) && (bc_type_xhi == "Outflow" || bc_type_xhi == "Open") && !solverChoice.use_real_bcs) {
57  ParallelFor(makeSlab(bx,0,dom_hi.x), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
58  {
59  pp_arr(i+1,j,k) = -pp_arr(i,j,k);
60  });
61  } else if (bx_hi.x == sub_hi.x) {
62  ParallelFor(makeSlab(bx,0,sub_hi.x), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
63  {
64  pp_arr(i+1,j,k) = pp_arr(i,j,k);
65  });
66  }
67 
68  if ( (bx_lo.y == dom_lo.y) && (bc_type_ylo == "Outflow" || bc_type_ylo == "Open") && !solverChoice.use_real_bcs) {
69  ParallelFor(makeSlab(bx,1,dom_lo.y), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
70  {
71  pp_arr(i,j-1,k) = -pp_arr(i,j,k);
72  });
73  } else if (bx_lo.y == sub_lo.y) {
74  ParallelFor(makeSlab(bx,1,sub_lo.y), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
75  {
76  pp_arr(i,j-1,k) = pp_arr(i,j,k);
77  });
78  }
79 
80  if ( (bx_hi.y == dom_hi.y) && (bc_type_yhi == "Outflow" || bc_type_yhi == "Open") && !solverChoice.use_real_bcs) {
81  ParallelFor(makeSlab(bx,1,dom_hi.y), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
82  {
83  pp_arr(i,j+1,k) = -pp_arr(i,j,k);
84  });
85  } else if (bx_hi.y == sub_hi.y) {
86  ParallelFor(makeSlab(bx,1,sub_hi.y), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
87  {
88  pp_arr(i,j+1,k) = pp_arr(i,j,k);
89  });
90  }
91 
92  // At low z we are always Neumann whether the box touches the bottom boundary or not
93  Box zbx(bx); zbx.grow(0,1); zbx.grow(1,1); // Grow in x-dir and y-dir because we have filled that above
94  if (bx_lo.z == sub_lo.z) {
95  ParallelFor(makeSlab(zbx,2,dom_lo.z), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
96  {
97  pp_arr(i,j,k-1) = pp_arr(i,j,k);
98  });
99  }
100 
101  if ( (bx_hi.z == dom_hi.z) && (bc_type_zhi == "Outflow" || bc_type_zhi == "Open") ) {
102  ParallelFor(makeSlab(bx,2,dom_hi.z), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
103  {
104  pp_arr(i,j,k+1) = -pp_arr(i,j,k);
105  });
106  } else if (bx_hi.z == sub_hi.z) {
107  ParallelFor(makeSlab(bx,2,sub_hi.z), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
108  {
109  pp_arr(i,j,k+1) = pp_arr(i,j,k);
110  });
111  }
112  } // mfi
113 
114  // Now overwrite with periodic fill outside domain and fine-fine fill inside
115  phi.FillBoundary(geom[lev].periodicity());
116 }
amrex::Array< std::string, 2 *AMREX_SPACEDIM > domain_bc_type
Definition: ERF.H:1084
Here is the call graph for this function:

◆ init1DArrays()

void ERF::init1DArrays ( )
private

◆ init_bcs()

void ERF::init_bcs ( )
private
300 {
301  bool rho_read = false;
302  bool read_prim_theta = true;
303  bool use_surfacelayer = false;
304 
305  init_phys_bcs(rho_read, read_prim_theta);
306 
307  Vector<Real> cons_dir_init(NBCVAR_max,zero);
308  cons_dir_init[BCVars::Rho_bc_comp] = one;
309  cons_dir_init[BCVars::RhoTheta_bc_comp] = -one;
310 
311  bool keqn_dir = (solverChoice.turbChoice[max_level].rans_type == RANSType::kEqn &&
312  solverChoice.turbChoice[max_level].dirichlet_k == true);
313  if (keqn_dir) {
314  // Need to change wall BC type, assume for now that all levels are RANS
315  for (int lev = 0; lev < max_level; ++lev) {
316  if (solverChoice.turbChoice[lev].rans_type != RANSType::kEqn) {
317  Error("If using one-eqn RANS, all levels must be RANS for now");
318  }
319  }
320  Print() << "Using dirichlet BC for k equation" << std::endl;
321  }
322 
323  // *****************************************************************************
324  //
325  // Here we translate the physical boundary conditions -- one type per face --
326  // into logical boundary conditions for each velocity component
327  //
328  // *****************************************************************************
329  {
330  domain_bcs_type.resize(AMREX_SPACEDIM+NBCVAR_max);
331  domain_bcs_type_d.resize(AMREX_SPACEDIM+NBCVAR_max);
332 
333  for (OrientationIter oit; oit; ++oit) {
334  Orientation ori = oit();
335  int dir = ori.coordDir();
336  Orientation::Side side = ori.faceDir();
337  auto const bct = phys_bc_type[ori];
338  if ( bct == ERF_BC::symmetry )
339  {
340  if (side == Orientation::low) {
341  for (int i = 0; i < AMREX_SPACEDIM; i++) {
343  }
345  } else {
346  for (int i = 0; i < AMREX_SPACEDIM; i++) {
348  }
350  }
351  }
352  else if (bct == ERF_BC::outflow or bct == ERF_BC::ho_outflow )
353  {
354  if (side == Orientation::low) {
355  for (int i = 0; i < AMREX_SPACEDIM; i++) {
357  }
358  if (!solverChoice.anelastic[0]) {
360  }
361  } else {
362  for (int i = 0; i < AMREX_SPACEDIM; i++) {
364  }
365  if (!solverChoice.anelastic[0]) {
367  }
368  }
369  }
370  else if (bct == ERF_BC::open)
371  {
372  if (side == Orientation::low) {
373  for (int i = 0; i < AMREX_SPACEDIM; i++)
375  } else {
376  for (int i = 0; i < AMREX_SPACEDIM; i++)
378  }
379  }
380  else if (bct == ERF_BC::inflow)
381  {
382  if (side == Orientation::low) {
383  for (int i = 0; i < AMREX_SPACEDIM; i++) {
385  if (input_bndry_planes && dir < 2 && m_r2d->ingested_velocity()) {
387  }
388  }
389  } else {
390  for (int i = 0; i < AMREX_SPACEDIM; i++) {
392  if (input_bndry_planes && dir < 2 && m_r2d->ingested_velocity()) {
394  }
395  }
396  }
397  }
398  else if (bct == ERF_BC::inflow_outflow)
399  {
400  if (side == Orientation::low) {
401  for (int i = 0; i < AMREX_SPACEDIM; i++) {
403  }
404  } else {
405  for (int i = 0; i < AMREX_SPACEDIM; i++) {
407  }
408  }
409  }
410  else if (bct == ERF_BC::no_slip_wall)
411  {
412  if (side == Orientation::low) {
413  for (int i = 0; i < AMREX_SPACEDIM; i++) {
415  }
416  } else {
417  for (int i = 0; i < AMREX_SPACEDIM; i++) {
419  }
420  }
421  }
422  else if (bct == ERF_BC::slip_wall)
423  {
424  if (side == Orientation::low) {
425  for (int i = 0; i < AMREX_SPACEDIM; i++) {
427  }
428  // Only normal direction has ext_dir
430 
431  } else {
432  for (int i = 0; i < AMREX_SPACEDIM; i++) {
434  }
435  // Only normal direction has ext_dir
437  }
438  }
439  else if (bct == ERF_BC::periodic)
440  {
441  if (side == Orientation::low) {
442  for (int i = 0; i < AMREX_SPACEDIM; i++) {
444  }
445  } else {
446  for (int i = 0; i < AMREX_SPACEDIM; i++) {
448  }
449  }
450  }
451  else if ( bct == ERF_BC::surface_layer )
452  {
453  use_surfacelayer = true;
454  AMREX_ALWAYS_ASSERT(dir == 2 && side == Orientation::low);
458  }
459  }
460  }
461 
462  // *****************************************************************************
463  //
464  // Here we translate the physical boundary conditions -- one type per face --
465  // into logical boundary conditions for each cell-centered variable
466  // (including the base state variables)
467  // NOTE: all "scalars" share the same type of boundary condition
468  //
469  // *****************************************************************************
470  {
471  for (OrientationIter oit; oit; ++oit) {
472  Orientation ori = oit();
473  int dir = ori.coordDir();
474  Orientation::Side side = ori.faceDir();
475  auto const bct = phys_bc_type[ori];
476  if ( bct == ERF_BC::symmetry )
477  {
478  if (side == Orientation::low) {
479  for (int i = 0; i < NBCVAR_max; i++) {
481  }
482  } else {
483  for (int i = 0; i < NBCVAR_max; i++) {
485  }
486  }
487  }
488  else if ( bct == ERF_BC::outflow )
489  {
490  if (side == Orientation::low) {
491  for (int i = 0; i < NBCVAR_max; i++) {
493  }
494  } else {
495  for (int i = 0; i < NBCVAR_max; i++) {
497  }
498  }
499  }
500  else if ( bct == ERF_BC::ho_outflow )
501  {
502  if (side == Orientation::low) {
503  for (int i = 0; i < NBCVAR_max; i++) {
505  }
506  } else {
507  for (int i = 0; i < NBCVAR_max; i++) {
509  }
510  }
511  }
512  else if ( bct == ERF_BC::open )
513  {
514  if (side == Orientation::low) {
515  for (int i = 0; i < NBCVAR_max; i++)
517  } else {
518  for (int i = 0; i < NBCVAR_max; i++)
520  }
521  }
522  else if ( bct == ERF_BC::no_slip_wall )
523  {
524  if (side == Orientation::low) {
525  for (int i = 0; i < NBCVAR_max; i++) {
527  if (m_bc_extdir_vals[BCVars::cons_bc+i][ori] != cons_dir_init[BCVars::cons_bc+i]) {
528  if (rho_read) {
530  } else {
532  }
533  }
534  }
535  if (std::abs(m_bc_neumann_vals[BCVars::RhoTheta_bc_comp][ori]) > zero) {
537  }
538  } else {
539  for (int i = 0; i < NBCVAR_max; i++) {
541  if (m_bc_extdir_vals[BCVars::cons_bc+i][ori] != cons_dir_init[BCVars::cons_bc+i]) {
542  if (rho_read) {
544  } else {
546  }
547  }
548  }
549  if (std::abs(m_bc_neumann_vals[BCVars::RhoTheta_bc_comp][ori]) > zero) {
551  }
552  }
553  }
554  else if (bct == ERF_BC::slip_wall)
555  {
556  if (side == Orientation::low) {
557  for (int i = 0; i < NBCVAR_max; i++) {
559  if (m_bc_extdir_vals[BCVars::cons_bc+i][ori] != cons_dir_init[BCVars::cons_bc+i]) {
560  if (rho_read) {
562  } else {
564  }
565  }
566  }
567  if (std::abs(m_bc_neumann_vals[BCVars::RhoTheta_bc_comp][ori]) > zero) {
569  }
570  if (std::abs(m_bc_neumann_vals[BCVars::Rho_bc_comp][ori]) > zero) {
572  }
573  } else {
574  for (int i = 0; i < NBCVAR_max; i++) {
576  if (m_bc_extdir_vals[BCVars::cons_bc+i][ori] != cons_dir_init[BCVars::cons_bc+i]) {
577  if (rho_read) {
579  } else {
581  }
582  }
583  }
584  if (std::abs(m_bc_neumann_vals[BCVars::RhoTheta_bc_comp][ori]) > zero) {
586  }
587  if (std::abs(m_bc_neumann_vals[BCVars::Rho_bc_comp][ori]) > zero) {
589  }
590  }
591  }
592  else if (bct == ERF_BC::inflow)
593  {
594  if (side == Orientation::low) {
595  for (int i = 0; i < NBCVAR_max; i++) {
597  if ((BCVars::cons_bc+i == RhoTheta_comp) &&
598  (th_bc_data[0].data() != nullptr))
599  {
600  if (read_prim_theta) domain_bcs_type[BCVars::cons_bc+i].setLo(dir, ERFBCType::ext_dir_prim);
601  }
602  else if (input_bndry_planes && dir < 2 && (
603  ( (BCVars::cons_bc+i == BCVars::Rho_bc_comp) && m_r2d->ingested_density()) ||
604  ( (BCVars::cons_bc+i == BCVars::RhoTheta_bc_comp) && m_r2d->ingested_theta() ) ||
605  ( (BCVars::cons_bc+i == BCVars::RhoKE_bc_comp) && m_r2d->ingested_KE() ) ||
606  ( (BCVars::cons_bc+i == BCVars::RhoScalar_bc_comp) && m_r2d->ingested_scalar() ) ||
607  ( (BCVars::cons_bc+i == BCVars::RhoQ1_bc_comp) && m_r2d->ingested_q1() ) ||
608  ( (BCVars::cons_bc+i == BCVars::RhoQ2_bc_comp) && m_r2d->ingested_q2() )) )
609  {
611  }
612  else if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
614  }
615  }
616  // Non-reflecting: extrapolate RhoTheta from interior so
617  // pressure is not prescribed, allowing acoustic waves out
618  if (m_bc_nonreflecting[ori]) {
620  }
621  } else {
622  for (int i = 0; i < NBCVAR_max; i++) {
624  if ((BCVars::cons_bc+i == RhoTheta_comp) &&
625  (th_bc_data[0].data() != nullptr))
626  {
627  if (read_prim_theta) domain_bcs_type[BCVars::cons_bc+i].setHi(dir, ERFBCType::ext_dir_prim);
628  }
629  else if (input_bndry_planes && dir < 2 && (
630  ( (BCVars::cons_bc+i == BCVars::Rho_bc_comp) && m_r2d->ingested_density()) ||
631  ( (BCVars::cons_bc+i == BCVars::RhoTheta_bc_comp) && m_r2d->ingested_theta() ) ||
632  ( (BCVars::cons_bc+i == BCVars::RhoKE_bc_comp) && m_r2d->ingested_KE() ) ||
633  ( (BCVars::cons_bc+i == BCVars::RhoScalar_bc_comp) && m_r2d->ingested_scalar() ) ||
634  ( (BCVars::cons_bc+i == BCVars::RhoQ1_bc_comp) && m_r2d->ingested_q1() ) ||
635  ( (BCVars::cons_bc+i == BCVars::RhoQ2_bc_comp) && m_r2d->ingested_q2() )
636  ) )
637  {
639  }
640  else if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
642  }
643  }
644  // Non-reflecting: extrapolate RhoTheta from interior
645  if (m_bc_nonreflecting[ori]) {
647  }
648  }
649  }
650  else if (bct == ERF_BC::inflow_outflow )
651  {
652  if (side == Orientation::low) {
653  for (int i = 0; i < NBCVAR_max; i++) {
655  if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
657  }
658  }
659  } else {
660  for (int i = 0; i < NBCVAR_max; i++) {
662  if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
664  }
665  }
666  }
667  }
668  else if (bct == ERF_BC::periodic)
669  {
670  if (side == Orientation::low) {
671  for (int i = 0; i < NBCVAR_max; i++) {
673  }
674  } else {
675  for (int i = 0; i < NBCVAR_max; i++) {
677  }
678  }
679  }
680  else if ( bct == ERF_BC::surface_layer )
681  {
682  AMREX_ALWAYS_ASSERT(dir == 2 && side == Orientation::low);
683  for (int i = 0; i < NBCVAR_max; i++) {
685  }
686  if (keqn_dir) {
687  Print() << "Setting surface layer logical BC to dirichlet for RANS with k model" << std::endl;
689  }
690  }
691  }
692  }
693 
694  // Sanity check that implicit diffusion is consistent with the BC types.
695  // Turn off implicit diffusion for a component if its BCs don't match
696  // those allowed by the tridiagonal solver.
697  const BCRec* bc_ptr = domain_bcs_type.data();
699  Vector<std::string> cc_comp_name = {"Theta", "KE", "Qv"};
700  for (int icomp(0); icomp < cc_comp_map.size(); ++icomp) {
701  int bc_comp = cc_comp_map[icomp];
702  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
703  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
704  bool neumann_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::neumann);
705  bool neumann_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::neumann);
706  if ( (!foextrap_on_zlo && !neumann_on_zlo && !use_surfacelayer) ||
707  (!foextrap_on_zhi && !neumann_on_zhi) ) {
708  Print() << "WARNING: Selected BCs for " << cc_comp_name[icomp] << " are not supported with implicit diffusion. Turning off implicit diffusion for this component." << "\n";
710  if (bc_comp == BCVars::RhoKE_bc_comp) { solverChoice.implicit_ke_diffusion = false; }
712  }
713  }
714  Vector<int> vel_comp_map = {BCVars::xvel_bc, BCVars::yvel_bc};
715  Vector<std::string> vel_comp_name = {"xvel", "yvel"};
716  for (int icomp(0); icomp < vel_comp_map.size(); ++icomp) {
717  int bc_comp = vel_comp_map[icomp];
718  bool ext_dir_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir ||
719  bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim);
720  bool ext_dir_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir ||
721  bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim);
722  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
723  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
724  if ( (!foextrap_on_zlo && !ext_dir_on_zlo && !use_surfacelayer) ||
725  (!foextrap_on_zhi && !ext_dir_on_zhi) ) {
726  Print() << "WARNING: Selected BCs for " << vel_comp_name[icomp] << " are not supported with implicit diffusion. Turning off implicit diffusion for this component." << "\n";
728  }
729  }
730 
731  // NOTE: Gpu:copy is a wrapper to htod_memcpy (GPU) or memcpy (CPU) and is a blocking comm
732  Gpu::copy(Gpu::hostToDevice, domain_bcs_type.begin(), domain_bcs_type.end(), domain_bcs_type_d.begin());
733 }
#define NBCVAR_max
Definition: ERF_IndexDefines.H:29
@ ho_outflow
@ inflow_outflow
amrex::Array< bool, AMREX_SPACEDIM *2 > m_bc_nonreflecting
Definition: ERF.H:1093
void init_phys_bcs(bool &rho_read, bool &read_prim_theta)
Definition: ERF_InitBCs.cpp:20
amrex::Array< amrex::Array< amrex::Real, AMREX_SPACEDIM *2 >, AMREX_SPACEDIM+NBCVAR_max > m_bc_neumann_vals
Definition: ERF.H:1090
@ RhoQ1_bc_comp
Definition: ERF_IndexDefines.H:91
@ RhoKE_bc_comp
Definition: ERF_IndexDefines.H:89
@ RhoTheta_bc_comp
Definition: ERF_IndexDefines.H:88
@ RhoQ2_bc_comp
Definition: ERF_IndexDefines.H:92
@ Rho_bc_comp
Definition: ERF_IndexDefines.H:87
@ neumann
Definition: ERF_IndexDefines.H:248
@ open
Definition: ERF_IndexDefines.H:250
@ reflect_odd
Definition: ERF_IndexDefines.H:239
@ hoextrap
Definition: ERF_IndexDefines.H:244
@ foextrap
Definition: ERF_IndexDefines.H:242
@ ext_dir
Definition: ERF_IndexDefines.H:243
@ ext_dir_prim
Definition: ERF_IndexDefines.H:246
@ ext_dir_upwind
Definition: ERF_IndexDefines.H:251
@ int_dir
Definition: ERF_IndexDefines.H:240
@ neumann_int
Definition: ERF_IndexDefines.H:249
@ reflect_even
Definition: ERF_IndexDefines.H:241
bool implicit_ke_diffusion
Definition: ERF_DataStruct.H:1251
bool implicit_moisture_diffusion
Definition: ERF_DataStruct.H:1250
bool implicit_thermal_diffusion
Definition: ERF_DataStruct.H:1249
bool implicit_momentum_diffusion
Definition: ERF_DataStruct.H:1252
Here is the call graph for this function:

◆ init_custom()

void ERF::init_custom ( int  lev)

Wrapper for custom problem-specific initialization routines that can be defined by the user as they set up a new problem in ERF. This wrapper handles all the overhead of defining the perturbation as well as initializing the random seed if needed.

This wrapper calls a user function to customize initialization on a per-Fab level inside an MFIter loop, so all the MultiFab operations are hidden from the user.

Parameters
levInteger specifying the current level
27 {
28  auto& lev_new = vars_new[lev];
29 
30  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp, 1);
31  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp, 1);
32 
33  MultiFab cons_pert(lev_new[Vars::cons].boxArray(), lev_new[Vars::cons].DistributionMap(),
34  lev_new[Vars::cons].nComp() , lev_new[Vars::cons].nGrow());
35  MultiFab xvel_pert(lev_new[Vars::xvel].boxArray(), lev_new[Vars::xvel].DistributionMap(), 1, lev_new[Vars::xvel].nGrowVect());
36  MultiFab yvel_pert(lev_new[Vars::yvel].boxArray(), lev_new[Vars::yvel].DistributionMap(), 1, lev_new[Vars::yvel].nGrowVect());
37  MultiFab zvel_pert(lev_new[Vars::zvel].boxArray(), lev_new[Vars::zvel].DistributionMap(), 1, lev_new[Vars::zvel].nGrowVect());
38 
39  // Default all perturbations to zero
40  cons_pert.setVal(0.);
41  xvel_pert.setVal(0.);
42  yvel_pert.setVal(0.);
43  zvel_pert.setVal(0.);
44 
45 
46 #ifdef _OPENMP
47 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
48 #endif
49  for (MFIter mfi(lev_new[Vars::cons], TileNoZ()); mfi.isValid(); ++mfi)
50  {
51  const Box &bx = mfi.tilebox();
52  const Box &xbx = mfi.tilebox(IntVect(1,0,0));
53  const Box &ybx = mfi.tilebox(IntVect(0,1,0));
54  const Box &zbx = mfi.tilebox(IntVect(0,0,1));
55 
56  const auto &cons_pert_arr = cons_pert.array(mfi);
57  const auto &xvel_pert_arr = xvel_pert.array(mfi);
58  const auto &yvel_pert_arr = yvel_pert.array(mfi);
59  const auto &zvel_pert_arr = zvel_pert.array(mfi);
60 
61  Array4<Real const> cons_arr = lev_new[Vars::cons].const_array(mfi);
62  Array4<Real const> z_nd_arr = (z_phys_nd[lev]) ? z_phys_nd[lev]->const_array(mfi) : Array4<Real const>{};
63  Array4<Real const> z_cc_arr = (z_phys_cc[lev]) ? z_phys_cc[lev]->const_array(mfi) : Array4<Real const>{};
64 
65  // Here we arbitrarily choose the x-oriented map factor -- this should be generalized
66  Array4<Real const> mf_m = mapfac[lev][MapFacType::m_x]->const_array(mfi);
67  Array4<Real const> mf_u = mapfac[lev][MapFacType::u_x]->const_array(mfi);
68  Array4<Real const> mf_v = mapfac[lev][MapFacType::v_y]->const_array(mfi);
69 
70  Array4<Real> r_hse_arr = r_hse.array(mfi);
71  Array4<Real> p_hse_arr = p_hse.array(mfi);
72 
73  prob->init_custom_pert(bx, cons_arr, cons_pert_arr,
74  r_hse_arr, p_hse_arr, z_nd_arr, z_cc_arr,
75  geom[lev].data(), mf_m, solverChoice, lev);
76  prob->init_custom_pert_vels(xbx, ybx, zbx,
77  xvel_pert_arr, yvel_pert_arr, zvel_pert_arr,
78  z_nd_arr, geom[lev].data(), mf_u, mf_v,
79  solverChoice, lev);
80 
81  // Zero out perturbations in covered cells in EB
82  if (solverChoice.terrain_type == TerrainType::EB) {
83 
84  Array4<const EBCellFlag> c_cellflg = (get_eb(lev).get_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
85  Array4<const EBCellFlag> u_cellflg = (get_eb(lev).get_u_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
86  Array4<const EBCellFlag> v_cellflg = (get_eb(lev).get_v_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
87  Array4<const EBCellFlag> w_cellflg = (get_eb(lev).get_w_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
88 
89  ParallelFor(bx,
90  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
91  if (c_cellflg(i,j,k).isCovered()) {
92  cons_pert_arr(i,j,k,RhoTheta_comp) = 0.0;
93  }
94  });
95 
97  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
98  if (u_cellflg(i,j,k).isCovered()) {
99  xvel_pert_arr(i,j,k) = 0.0;
100  }
101  },
102  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
103  if (v_cellflg(i,j,k).isCovered()) {
104  yvel_pert_arr(i,j,k) = 0.0;
105  }
106  },
107  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
108  if (w_cellflg(i,j,k).isCovered()) {
109  zvel_pert_arr(i,j,k) = 0.0;
110  }
111  });
112  }
113 
114  } //mfi
115 
116 
117  // Add problem-specific perturbation to background flow if not doing anelastic with fixed-in-time density
118  if (!solverChoice.fixed_density[lev]) {
119  MultiFab::Add(lev_new[Vars::cons], cons_pert, Rho_comp, Rho_comp, 1, cons_pert.nGrow());
120  }
121  MultiFab::Add(lev_new[Vars::cons], cons_pert, RhoTheta_comp, RhoTheta_comp, 1, cons_pert.nGrow());
122  MultiFab::Add(lev_new[Vars::cons], cons_pert, RhoScalar_comp,RhoScalar_comp,NSCALARS, cons_pert.nGrow());
123 
124  // RhoKE is relevant if using Deardorff with LES, k-equation for RANS, MYJ, SHOC, MYNN2.5 or MYNN-EDMF
125  // Here we initialize TKE to the tke_min value and then add problem-specific perturbations
126  if (solverChoice.turbChoice[lev].use_tke) {
127  lev_new[Vars::cons].setVal(solverChoice.turbChoice[lev].tke_min, RhoKE_comp, 1);
128  MultiFab::Multiply(lev_new[Vars::cons],lev_new[Vars::cons],Rho_comp,RhoKE_comp,1,lev_new[Vars::cons].nGrowVect());
129  MultiFab::Add(lev_new[Vars::cons], cons_pert, RhoKE_comp, RhoKE_comp, 1, cons_pert.nGrow());
130  }
131 
132  if (solverChoice.moisture_type != MoistureType::None) {
133  int qstate_size = micro->Get_Qstate_Size();
134  for (int q_offset(0); q_offset<qstate_size; ++q_offset) {
135  int q_idx = RhoQ1_comp+q_offset;
136  MultiFab::Add(lev_new[Vars::cons], cons_pert, q_idx, q_idx, 1, cons_pert.nGrow());
137  }
138  }
139 
140  // Should we initialize the velocities from a checkpoint file?
141  static std::string init_vels_from_checkpoint;
142  ParmParse pp("erf");
143  if (pp.query("init_vels_from_checkpoint",init_vels_from_checkpoint)) {
144  ReadVelsOnlyFromCheckpointFile(lev,init_vels_from_checkpoint);
145  } else {
146  MultiFab::Add(lev_new[Vars::xvel], xvel_pert, 0, 0, 1, xvel_pert.nGrowVect());
147  MultiFab::Add(lev_new[Vars::yvel], yvel_pert, 0, 0, 1, yvel_pert.nGrowVect());
148  MultiFab::Add(lev_new[Vars::zvel], zvel_pert, 0, 0, 1, zvel_pert.nGrowVect());
149  }
150 
151  // If initializing for ensemble simluations, then
152  // 1. Create cell-centered random perturbations
153  // 2. Create cell-centered spatially correlated perturbations
154  // 3. Read in the coarse background state from the coarse data file,
155  // interpolate the state data onto the current mesh, and
156  // add the perturbations to the background state and then populate the "pert variables
157 
159  MultiFab mf_cc_pert;
160  create_random_perturbations(lev, mf_cc_pert);
162  create_background_state_for_ensemble(lev, mf_cc_pert, lev_new[Vars::cons], lev_new[Vars::xvel], lev_new[Vars::yvel], lev_new[Vars::zvel]);
163  }
164 }
const Box xbx
Definition: ERF_SetupDiff.H:7
const Box ybx
Definition: ERF_SetupDiff.H:8
void create_background_state_for_ensemble(int lev, amrex::MultiFab &mf_cc_pert, amrex::MultiFab &cons_pert, amrex::MultiFab &xvel_pert, amrex::MultiFab &yvel_pert, amrex::MultiFab &zvel_pert)
Definition: ERF_InitForEnsemble.cpp:515
void apply_gaussian_smoothing_to_perturbations(const int lev, amrex::MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:86
void ReadVelsOnlyFromCheckpointFile(int lev_to_fill, std::string &chkfile)
Definition: ERF_Checkpoint.cpp:1182
void create_random_perturbations(const int lev, amrex::MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:14
eb_aux_ const * get_v_const_factory() const noexcept
Definition: ERF_EB.H:51
eb_aux_ const * get_u_const_factory() const noexcept
Definition: ERF_EB.H:50
amrex::Vector< int > fixed_density
Definition: ERF_DataStruct.H:1230
bool is_init_for_ensemble
Definition: ERF_DataStruct.H:1446
Here is the call graph for this function:

◆ init_Dirichlet_bc_data()

void ERF::init_Dirichlet_bc_data ( const std::string  input_file)
private
736 {
737  // Read the dirichlet_input file
738  Print() << "dirichlet_input file location : " << input_file << std::endl;
739  std::ifstream input_reader(input_file);
740  if (!input_reader.is_open()) {
741  amrex::Abort("Error opening the dirichlet_input file.\n");
742  }
743 
744  Print() << "Successfully opened the dirichlet_input file. Now reading... " << std::endl;
745  std::string line;
746 
747  // Size of Ninp (number of z points in input file)
748  Vector<Real> z_inp_tmp, u_inp_tmp, v_inp_tmp, w_inp_tmp, th_inp_tmp;
749 
750  // Top and bot for domain
751  const int klo = geom[0].Domain().smallEnd()[2];
752  const int khi = geom[0].Domain().bigEnd()[2];
753  const Real zbot = zlevels_stag[0][klo];
754  const Real ztop = zlevels_stag[0][khi+1];
755 
756  // Flag if theta input
757  Real th_init = -Real(300.0);
758  bool th_read{false};
759 
760  // Add surface
761  z_inp_tmp.push_back(zbot); // height above sea level [m]
762  u_inp_tmp.push_back(zero);
763  v_inp_tmp.push_back(zero);
764  w_inp_tmp.push_back(zero);
765  th_inp_tmp.push_back(th_init);
766 
767  // Read the vertical profile at each given height
768  Real z, u, v, w, th;
769  while(std::getline(input_reader, line)) {
770  std::istringstream iss_z(line);
771 
772  Vector<Real> rval_v;
773  Real rval;
774  while (iss_z >> rval) {
775  rval_v.push_back(rval);
776  }
777  if ((rval_v.size() != 4) && (rval_v.size() != 5)) {
778  Abort("Unknown inflow file format!");
779  }
780  z = rval_v[0];
781  u = rval_v[1];
782  v = rval_v[2];
783  w = rval_v[3];
784 
785  // Format without theta
786  if (rval_v.size() == 4) {
787  if (z == zbot) {
788  u_inp_tmp[0] = u;
789  v_inp_tmp[0] = v;
790  w_inp_tmp[0] = w;
791  } else {
792  AMREX_ALWAYS_ASSERT(z > z_inp_tmp[z_inp_tmp.size()-1]); // sounding is increasing in height
793  z_inp_tmp.push_back(z);
794  u_inp_tmp.push_back(u);
795  v_inp_tmp.push_back(v);
796  w_inp_tmp.push_back(w);
797  if (z >= ztop) break;
798  }
799  } else if (rval_v.size() == 5) {
800  th_read = true;
801  th = rval_v[4];
802  if (z == zbot) {
803  u_inp_tmp[0] = u;
804  v_inp_tmp[0] = v;
805  w_inp_tmp[0] = w;
806  th_inp_tmp[0] = th;
807  } else {
808  AMREX_ALWAYS_ASSERT(z > z_inp_tmp[z_inp_tmp.size()-1]); // sounding is increasing in height
809  z_inp_tmp.push_back(z);
810  u_inp_tmp.push_back(u);
811  v_inp_tmp.push_back(v);
812  w_inp_tmp.push_back(w);
813  th_inp_tmp.push_back(th);
814  if (z >= ztop) break;
815  }
816  } else {
817  Abort("Unknown inflow file format!");
818  }
819  }
820 
821  // Ensure we set a reasonable theta surface
822  if (th_read) {
823  if (th_inp_tmp[0] == th_init) {
824  AMREX_ALWAYS_ASSERT_WITH_MESSAGE((th_inp_tmp.size() > 2) && (z_inp_tmp.size() > 2),
825  "Need at least 3 theta profile points to extrapolate surface theta");
826  Real slope = (th_inp_tmp[2] - th_inp_tmp[1]) / (z_inp_tmp[2] - z_inp_tmp[1]);
827  Real dz = z_inp_tmp[0] - z_inp_tmp[1];
828  th_inp_tmp[0] = slope * dz + th_inp_tmp[1];
829  }
830  }
831 
832  amrex::Print() << "Successfully read and interpolated the dirichlet_input file..." << std::endl;
833  input_reader.close();
834 
835  for (int lev = 0; lev <= max_level; lev++) {
836 
837  const int Nz = geom[lev].Domain().size()[2];
838 
839  // Size of Nz (domain grid)
840  Vector<Real> zcc_inp(Nz );
841  Vector<Real> znd_inp(Nz+1);
842  Vector<Real> u_inp(Nz ); xvel_bc_data[lev].resize(Nz ,zero);
843  Vector<Real> v_inp(Nz ); yvel_bc_data[lev].resize(Nz ,zero);
844  Vector<Real> w_inp(Nz+1); zvel_bc_data[lev].resize(Nz+1,zero);
845  Vector<Real> th_inp;
846  if (th_read) {
847  th_inp.resize(Nz);
848  th_bc_data[lev].resize(Nz, zero);
849  }
850 
851  // At this point, we have an input from zbot up to
852  // z_inp_tmp[N-1] >= ztop. Now, interpolate to grid level 0 heights
853  const int Ninp = z_inp_tmp.size();
854  for (int k(0); k<Nz; ++k) {
855  zcc_inp[k] = myhalf * (zlevels_stag[lev][k] + zlevels_stag[lev][k+1]);
856  znd_inp[k] = zlevels_stag[lev][k+1];
857  u_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), u_inp_tmp.dataPtr(), zcc_inp[k], Ninp);
858  v_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), v_inp_tmp.dataPtr(), zcc_inp[k], Ninp);
859  w_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), w_inp_tmp.dataPtr(), znd_inp[k], Ninp);
860  if (th_read) {
861  th_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), th_inp_tmp.dataPtr(), zcc_inp[k], Ninp);
862  }
863  }
864  znd_inp[Nz] = ztop;
865  w_inp[Nz] = interpolate_1d(z_inp_tmp.dataPtr(), w_inp_tmp.dataPtr(), ztop, Ninp);
866 
867  // Copy host data to the device
868  Gpu::copy(Gpu::hostToDevice, u_inp.begin(), u_inp.end(), xvel_bc_data[lev].begin());
869  Gpu::copy(Gpu::hostToDevice, v_inp.begin(), v_inp.end(), yvel_bc_data[lev].begin());
870  Gpu::copy(Gpu::hostToDevice, w_inp.begin(), w_inp.end(), zvel_bc_data[lev].begin());
871  if (th_read) {
872  Gpu::copy(Gpu::hostToDevice, th_inp.begin(), th_inp.end(), th_bc_data[lev].begin());
873  }
874 
875  // NOTE: These device vectors are passed to the PhysBC constructors when that
876  // class is instantiated in ERF_MakeNewArrays.cpp.
877  } // lev
878 }
const Real ztop
Definition: ERF_InitCustomPertVels_ParticleTests.H:4
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real interpolate_1d(const amrex::Real *alpha, const amrex::Real *beta, const amrex::Real alpha_interp, const int alpha_size)
Definition: ERF_Interpolation_1D.H:14
Here is the call graph for this function:

◆ init_from_hse()

void ERF::init_from_hse ( int  lev)

Initialize the background flow to have the calculated HSE density and rho*theta calculated from the HSE pressure. In general, the hydrostatically balanced density and pressure (r_hse and p_hse from base_state) used here may be calculated through a solver path such as:

ERF::initHSE(lev)

  • call prob->erf_init_dens_hse_dry(...)
    • call Problem::init_isentropic_hse(...), to simultaneously calculate r_hse and p_hse with Newton iteration – assuming constant theta
    • save r_hse
  • call ERF::enforce_hse(...), calculates p_hse from saved r_hse (redundant, but needed because p_hse is not necessarily calculated by the Problem implementation) and pi_hse and th_hse – note: this pressure does not exactly match the p_hse from before because what is calculated by init_isentropic_hse comes from the EOS whereas what is calculated here comes from the hydro- static equation
Parameters
levInteger specifying the current level
33 {
34  auto& lev_new = vars_new[lev];
35 
36  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp, 1);
37  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp, 1);
38 
39 #ifdef _OPENMP
40 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
41 #endif
42  for (MFIter mfi(lev_new[Vars::cons], TileNoZ()); mfi.isValid(); ++mfi)
43  {
44  const Box &gbx = mfi.growntilebox(1);
45  const Array4<Real >& cons_arr = lev_new[Vars::cons].array(mfi);
46  const Array4<Real const>& r_hse_arr = r_hse.const_array(mfi);
47  const Array4<Real const>& p_hse_arr = p_hse.const_array(mfi);
48 
49  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
50  {
51  cons_arr(i,j,k,Rho_comp) = r_hse_arr(i,j,k);
52  cons_arr(i,j,k,RhoTheta_comp) = getRhoThetagivenP(p_hse_arr(i,j,k));
53  });
54  } //mfi
55 }
Here is the call graph for this function:

◆ init_from_input_sounding()

void ERF::init_from_input_sounding ( int  lev)

High level wrapper for initializing scalar and velocity level data from input sounding data.

Parameters
levInteger specifying the current level
54 {
55  // We only want to read the file once -- here we fill one FArrayBox (per variable) that spans the domain
56  if (lev == 0) {
58  Error("input_sounding file name must be provided via input");
59  }
60 
62 
63  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
64 
65  // this will interpolate the input profiles to the nominal height levels
66  // (ranging from 0 to the domain top)
67  for (int n = 0; n < input_sounding_data.n_sounding_files; n++) {
68  input_sounding_data.read_from_file(geom[lev], zlevels_stag[lev], n, is_moist);
69  }
70 
71  // this will calculate the hydrostatically balanced density and pressure
72  // profiles following WRF ideal.exe
73  if (solverChoice.sounding_type == SoundingType::Ideal) {
75  } else if (solverChoice.sounding_type == SoundingType::Isentropic ||
76  solverChoice.sounding_type == SoundingType::DryIsentropic) {
77  input_sounding_data.assume_dry = (solverChoice.sounding_type == SoundingType::DryIsentropic);
79  }
80 
81  } else {
82  //
83  // We need to do this interp from coarse level in order to set the values of
84  // the base state inside the domain but outside of the fine region
85  //
86  base_state[lev-1].FillBoundary(geom[lev-1].periodicity());
87  //
88  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
89  // have been pre-filled - this includes ghost cells both inside and outside
90  // the domain
91  //
92  InterpFromCoarseLevel(base_state[lev], base_state[lev].nGrowVect(),
93  IntVect(0,0,0), // do not fill ghost cells outside the domain
94  base_state[lev-1], 0, 0, base_state[lev].nComp(),
95  geom[lev-1], geom[lev],
96  refRatio(lev-1), &cell_cons_interp,
98 
99  // We need to do this here because the interpolation above may leave corners unfilled
100  // when the corners need to be filled by, for example, reflection of the fine ghost
101  // cell outside the fine region but inide the domain.
102  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
103  }
104 
105  auto& lev_new = vars_new[lev];
106 
107  const bool l_isentropic = (solverChoice.sounding_type == SoundingType::Isentropic ||
108  solverChoice.sounding_type == SoundingType::DryIsentropic);
109 
110  const bool constant_density_sounding = (solverChoice.sounding_type == SoundingType::ConstantDensity);
111 
112  MultiFab r_hse (base_state[lev], make_alias, BaseState::r0_comp, 1);
113  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
114  MultiFab pi_hse(base_state[lev], make_alias, BaseState::pi0_comp, 1);
115  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
116  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp, 1);
117 
118  const Real l_gravity = solverChoice.gravity;
119  const Real l_rdOcp = solverChoice.rdOcp;
120  const bool l_moist = (solverChoice.moisture_type != MoistureType::None);
121 
122  int ngz = r_hse.nGrow(2);
123 
124 #ifdef _OPENMP
125 #pragma omp parallel if (Gpu::notInLaunchRegion())
126 #endif
127  for (MFIter mfi(lev_new[Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
128  const Box &bx = mfi.tilebox();
129  const auto &cons_arr = lev_new[Vars::cons].array(mfi);
130  Array4<Real> r_hse_arr = r_hse.array(mfi);
131  Array4<Real> p_hse_arr = p_hse.array(mfi);
132  Array4<Real> pi_hse_arr = pi_hse.array(mfi);
133  Array4<Real> th_hse_arr = th_hse.array(mfi);
134  Array4<Real> qv_hse_arr = qv_hse.array(mfi);
135 
136  Array4<Real const> z_cc_arr = (z_phys_cc[lev]) ? z_phys_cc[lev]->const_array(mfi) : Array4<Real const>{};
137 
138  if (constant_density_sounding) {
139  // This assumes rho_0 = one
140  // HSE will be calculated later with call to initHSE
141  init_state_from_input_sounding(bx, cons_arr, geom[lev].data(), z_cc_arr,
142  l_moist, input_sounding_data);
143  }
144  else
145  {
146  // HSE will be initialized here, interpolated from values previously
147  // calculated by calc_rho_p or calc_rho_p_isentropic
149  r_hse_arr, p_hse_arr, pi_hse_arr, th_hse_arr, qv_hse_arr,
150  geom[lev].data(), z_cc_arr,
151  l_gravity, l_rdOcp, l_moist, input_sounding_data,
152  l_isentropic, ngz);
153  }
154  }
155 
156  if (!constant_density_sounding) {
157  // Enforce HSE on the base state -- holding th_hse and qv_hse constant
158  bool use_existing_sfc_density = true;
159  rebalance_columns(r_hse, th_hse, qv_hse, qv_hse, z_phys_nd[lev].get(), geom[lev], use_existing_sfc_density);
160 
161  // Update rho in the state from base state
162  MultiFab::Copy(lev_new[Vars::cons], r_hse, 0, Rho_comp, 1, 1);
163 
164  // Update (rho theta) in the state from base state
165  MultiFab::Copy(lev_new[Vars::cons], th_hse, 0, RhoTheta_comp, 1, 1);
166  MultiFab::Multiply(lev_new[Vars::cons], lev_new[Vars::cons], Rho_comp, RhoTheta_comp, 1, 1);
167 
168  if (l_moist) {
169  // Update (rho qv) in the state from base state
170  MultiFab::Copy(lev_new[Vars::cons], qv_hse, 0, RhoQ1_comp, 1, 1);
171  MultiFab::Multiply(lev_new[Vars::cons], lev_new[Vars::cons], Rho_comp, RhoQ1_comp, 1, 1);
172  }
173  }
174 
175  for (MFIter mfi(lev_new[Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi)
176  {
177  const Box &bx = mfi.tilebox();
178  const auto& xvel_arr = lev_new[Vars::xvel].array(mfi);
179  const auto& yvel_arr = lev_new[Vars::yvel].array(mfi);
180  const auto& zvel_arr = lev_new[Vars::zvel].array(mfi);
181  const auto& z_nd_arr = (z_phys_nd[lev]) ? z_phys_nd[lev]->const_array(mfi) : Array4<Real const>{};
182  init_velocities_from_input_sounding(bx, xvel_arr, yvel_arr, zvel_arr,
183  geom[lev].data(), z_nd_arr, input_sounding_data);
184  }
185 
186  // *****************************************************************************
187  // Re-compute p_hse and pi_hse
188  // *****************************************************************************
189  for (MFIter mfi(lev_new[Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi)
190  {
191  Box bx = mfi.tilebox();
192 
193  const Array4<const Real>& r_hse_arr = r_hse.const_array(mfi);
194  const Array4<const Real>& th_hse_arr = th_hse.const_array(mfi);
195  const Array4<const Real>& qv_hse_arr = qv_hse.const_array(mfi);
196 
197  const Array4< Real>& p_hse_arr = p_hse.array(mfi);
198  const Array4< Real>& pi_hse_arr = pi_hse.array(mfi);
199 
200  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
201  {
202  Real rhotheta = r_hse_arr(i,j,k) * th_hse_arr(i,j,k);
203  p_hse_arr(i,j,k) = getPgivenRTh(rhotheta,qv_hse_arr(i,j,k));
204  pi_hse_arr(i,j,k) = getExnergivenRTh(rhotheta, l_rdOcp, qv_hse_arr(i,j,k));
205  });
206  }
207 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenRTh(const amrex::Real rhotheta, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:156
void init_state_from_input_sounding_hse(const Box &bx, Array4< Real > const &state, Array4< Real > const &r_hse_arr, Array4< Real > const &p_hse_arr, Array4< Real > const &pi_hse_arr, Array4< Real > const &th_hse_arr, Array4< Real > const &qv_hse_arr, GeometryData const &geomdata, Array4< const Real > const &z_cc_arr, const Real &l_gravity, const Real &l_rdOcp, const bool &l_moist, InputSoundingData const &inputSoundingData, const bool &l_isentropic, const int &ngz)
Definition: ERF_InitFromInputSounding.cpp:275
void init_velocities_from_input_sounding(const Box &bx, Array4< Real > const &x_vel, Array4< Real > const &y_vel, Array4< Real > const &z_vel, GeometryData const &geomdata, Array4< const Real > const &z_nd_arr, InputSoundingData const &inputSoundingData)
Definition: ERF_InitFromInputSounding.cpp:405
void init_state_from_input_sounding(const Box &bx, Array4< Real > const &state, GeometryData const &geomdata, Array4< const Real > const &z_cc_arr, const bool &l_moist, InputSoundingData const &inputSoundingData)
Definition: ERF_InitFromInputSounding.cpp:219
void rebalance_columns(MultiFab &rho, const MultiFab &theta, const MultiFab &qv, const MultiFab &qt, const MultiFab *z_phys, const Geometry &geom, bool use_existing_sfc_density)
Definition: ERF_Rebalance.cpp:7
InputSoundingData input_sounding_data
Definition: ERF.H:854
@ rho0_bc_comp
Definition: ERF_IndexDefines.H:113
void resize_arrays()
Definition: ERF_InputSoundingData.H:60
int n_sounding_files
Definition: ERF_InputSoundingData.H:400
amrex::Vector< std::string > input_sounding_file
Definition: ERF_InputSoundingData.H:398
void calc_rho_p(int itime)
Definition: ERF_InputSoundingData.H:178
void calc_rho_p_isentropic(int itime)
Definition: ERF_InputSoundingData.H:264
void read_from_file(const amrex::Geometry &geom, const amrex::Vector< amrex::Real > &zlevels_stag, int itime, bool is_moist)
Definition: ERF_InputSoundingData.H:77
bool assume_dry
Definition: ERF_InputSoundingData.H:403
static SoundingType sounding_type
Definition: ERF_DataStruct.H:1199
Here is the call graph for this function:

◆ init_geo_wind_profile()

void ERF::init_geo_wind_profile ( const std::string  input_file,
amrex::Vector< amrex::Real > &  u_geos,
amrex::Gpu::DeviceVector< amrex::Real > &  u_geos_d,
amrex::Vector< amrex::Real > &  v_geos,
amrex::Gpu::DeviceVector< amrex::Real > &  v_geos_d,
const amrex::Geometry &  lgeom,
const amrex::Vector< amrex::Real > &  zlev_stag 
)
private
17 {
18  const int klo = 0;
19  const int khi = lgeom.Domain().bigEnd()[AMREX_SPACEDIM-1];
20  const amrex::Real dz = lgeom.CellSize()[AMREX_SPACEDIM-1];
21 
22  const bool grid_stretch = (zlev_stag.size() > 0);
23  const Real zbot = (grid_stretch) ? zlev_stag[klo] : lgeom.ProbLo(AMREX_SPACEDIM-1);
24  const Real ztop = (grid_stretch) ? zlev_stag[khi+1] : lgeom.ProbHi(AMREX_SPACEDIM-1);
25 
26  amrex::Print() << "Reading geostrophic wind profile from " << input_file << std::endl;
27  std::ifstream profile_reader(input_file);
28  if(!profile_reader.is_open()) {
29  amrex::Error("Error opening the abl_geo_wind_table\n");
30  }
31 
32  // First, read the input data into temp vectors
33  std::string line;
34  Vector<Real> z_inp, Ug_inp, Vg_inp;
35  Real z, Ug, Vg;
36  amrex::Print() << "z Ug Vg" << std::endl;
37  while(std::getline(profile_reader, line)) {
38  std::istringstream iss(line);
39  iss >> z >> Ug >> Vg;
40  amrex::Print() << z << " " << Ug << " " << Vg << std::endl;
41  z_inp.push_back(z);
42  Ug_inp.push_back(Ug);
43  Vg_inp.push_back(Vg);
44  if (z >= ztop) break;
45  }
46 
47  const int Ninp = z_inp.size();
48  AMREX_ALWAYS_ASSERT(z_inp[0] <= zbot);
49  AMREX_ALWAYS_ASSERT(z_inp[Ninp-1] >= ztop);
50 
51  // Now, interpolate vectors to the cell centers
52  for (int k = 0; k <= khi; k++) {
53  z = (grid_stretch) ? myhalf * (zlev_stag[k] + zlev_stag[k+1])
54  : zbot + (k + myhalf) * dz;
55  u_geos[k] = interpolate_1d(z_inp.dataPtr(), Ug_inp.dataPtr(), z, Ninp);
56  v_geos[k] = interpolate_1d(z_inp.dataPtr(), Vg_inp.dataPtr(), z, Ninp);
57  }
58 
59  // Copy from host version to device version
60  Gpu::copy(Gpu::hostToDevice, u_geos.begin(), u_geos.end(), u_geos_d.begin());
61  Gpu::copy(Gpu::hostToDevice, v_geos.begin(), v_geos.end(), v_geos_d.begin());
62 
63  profile_reader.close();
64 }
Here is the call graph for this function:

◆ init_immersed_forcing()

void ERF::init_immersed_forcing ( int  lev)

Set velocities in cells that are immersed to be 0 (or a very small number)

Parameters
levInteger specifying the current level
16 {
17  auto& lev_new = vars_new[lev];
18  MultiFab* terrain_blank = terrain_blanking[lev].get();
19 
20 #ifdef _OPENMP
21 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
22 #endif
23  for (MFIter mfi(lev_new[Vars::cons], TileNoZ()); mfi.isValid(); ++mfi)
24  {
25  const Box &xbx = mfi.tilebox(IntVect(1,0,0));
26  const Box &ybx = mfi.tilebox(IntVect(0,1,0));
27  const Box &zbx = mfi.tilebox(IntVect(0,0,1));
28  const Real epsilon = Real(1e-2);
29 
30  const Array4<const Real>& t_blank_arr = terrain_blank->const_array(mfi);
31 
32  const auto &xvel_arr = lev_new[Vars::xvel].array(mfi);
33  const auto &yvel_arr = lev_new[Vars::yvel].array(mfi);
34  const auto &zvel_arr = lev_new[Vars::zvel].array(mfi);
35 
36  // Set the x,y,z-velocities
38  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
39  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i-1, j, k));
40  if (t_blank == one) { xvel_arr(i, j, k) = epsilon; }
41  },
42  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
43  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j-1, k));
44  if (t_blank == one) { yvel_arr(i, j, k) = epsilon; }
45  },
46  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
47  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j, k-1));
48  if (t_blank == one) { zvel_arr(i, j, k) = epsilon; }
49  });
50  } //mfi
51 }
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Here is the call graph for this function:

◆ init_only()

void ERF::init_only ( int  lev,
amrex::Real  time 
)
1823 {
1824  t_new[lev] = elapsed_time;
1825  t_old[lev] = elapsed_time - bogus_large_value;
1826 
1827  auto& lev_new = vars_new[lev];
1828  auto& lev_old = vars_old[lev];
1829 
1830  // Loop over grids at this level to initialize our grid data
1831  lev_new[Vars::cons].setVal(0.0); lev_old[Vars::cons].setVal(0.0);
1832  lev_new[Vars::xvel].setVal(0.0); lev_old[Vars::xvel].setVal(0.0);
1833  lev_new[Vars::yvel].setVal(0.0); lev_old[Vars::yvel].setVal(0.0);
1834  lev_new[Vars::zvel].setVal(0.0); lev_old[Vars::zvel].setVal(0.0);
1835 
1836  // Initialize background flow (optional)
1837  if (solverChoice.init_type == InitType::Input_Sounding) {
1838  // The physbc's need the terrain but are needed for initHSE
1839  // We have already made the terrain in the call to init_zphys
1840  // in MakeNewLevelFromScratch
1841  make_physbcs(lev);
1842 
1843  // Now init the base state and the data itself
1845 
1846  // The base state has been initialized by integrating vertically
1847  // through the sounding for ideal (like WRF) or isentropic approaches
1848  if (solverChoice.sounding_type == SoundingType::Ideal ||
1849  solverChoice.sounding_type == SoundingType::Isentropic ||
1850  solverChoice.sounding_type == SoundingType::DryIsentropic) {
1851  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(solverChoice.use_gravity,
1852  "Gravity should be on to be consistent with sounding initialization.");
1853  } else { // SoundingType::ConstantDensity
1854  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!solverChoice.use_gravity || (solverChoice.anelastic[lev] == 1),
1855  "Constant density probably doesn't make sense for compressible flow with gravity");
1856  initHSE();
1857  }
1858 
1859 #ifdef ERF_USE_NETCDF
1860  }
1861  else if (solverChoice.init_type == InitType::WRFInput && !nc_init_file[lev].empty())
1862  {
1863  // The base state is initialized from WRF wrfinput data, output by
1864  // ideal.exe or real.exe
1865 
1866  init_from_wrfinput(lev, *mf_PSFC[lev]);
1867 
1868  // The physbc's need the terrain but are needed for initHSE
1869  make_physbcs(lev);
1870  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
1871  }
1872  else if (solverChoice.init_type == InitType::WRFInput && nc_init_file[lev].empty())
1873  {
1874  amrex::Abort("This pathway is not quite implemented yet");
1875  }
1876  else if (solverChoice.init_type == InitType::NCFile)
1877  {
1878  // The state is initialized by reading from a Netcdf file
1879  init_from_ncfile(lev);
1880 
1881  // The physbc's need the terrain but are needed for initHSE
1882  make_physbcs(lev);
1883  }
1884  else if (solverChoice.init_type == InitType::Metgrid)
1885  {
1886  // The base state is initialized from data output by WPS metgrid;
1887  // we will rebalance after interpolation
1888  init_from_metgrid(lev);
1889 #endif
1890  } else if ( (solverChoice.init_type == InitType::Uniform ) ||
1891  (solverChoice.init_type == InitType::ConstantDensity) ||
1892  (solverChoice.init_type == InitType::Isentropic ) ||
1893  (solverChoice.init_type == InitType::ConstantDensityLinearTheta ) ||
1894  (solverChoice.init_type == InitType::HindCast ) ||
1895  (solverChoice.init_type == InitType::MoistBaseState ) ) {
1896  // Initialize a uniform density/entropy background field and base state
1897  // based on the problem-specified reference density and temperature
1898 
1899  // The physbc's need the terrain but are needed for initHSE
1900  make_physbcs(lev);
1901 
1902  // We will initialize the state from the background state so must set that first
1903  // The choice between constant rho and constant theta will be made inside initHSE
1904  initHSE(lev);
1905 
1906  // Copy rho and rhotheta from rho_hse and p_hse
1907  init_from_hse(lev);
1908 
1909  } else {
1910  Abort("Unknown init_type!");
1911  }
1912 
1913  // Add problem-specific flow features
1914  //
1915  // Notes:
1916  // - This calls init_custom_pert that is defined for each problem
1917  // - This may modify the base state
1918  // - The fields set by init_custom_pert are **perturbations** to the
1919  // background flow set based on init_type
1920  if (solverChoice.init_type != InitType::NCFile) {
1921  init_custom(lev);
1922  }
1923 
1924  // Ensure that the face-based data are the same on both sides of a periodic domain.
1925  // The data associated with the lower grid ID is considered the correct value.
1926  lev_new[Vars::xvel].OverrideSync(geom[lev].periodicity());
1927  lev_new[Vars::yvel].OverrideSync(geom[lev].periodicity());
1928  lev_new[Vars::zvel].OverrideSync(geom[lev].periodicity());
1929 
1930  if (solverChoice.spongeChoice.sponge_type == SpongeType::Input_Sponge)
1931  {
1932  input_sponge(lev);
1933  }
1934 
1935  // Initialize turbulent perturbation
1936  if (solverChoice.use_perturbation(lev)) {
1937  turbPert_update(lev, zero);
1938  turbPert_amplitude(lev);
1939  }
1940 
1941  // Set initial velocity field for immersed cells to be close to 0
1942  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
1943  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
1944  init_immersed_forcing(lev);
1945  }
1946 }
void init_from_input_sounding(int lev)
Definition: ERF_InitFromInputSounding.cpp:53
void init_custom(int lev)
Definition: ERF_InitCustomPertState.cpp:26
void init_from_hse(int lev)
Definition: ERF_InitFromHSE.cpp:32
void initHSE()
Initialize HSE.
Definition: ERF_Init1D.cpp:192
void turbPert_update(const int lev, const amrex::Real dt)
Definition: ERF_InitTurbPert.cpp:12
void input_sponge(int lev)
Definition: ERF_InitSponge.cpp:17
void make_physbcs(int lev)
Definition: ERF_MakeNewArrays.cpp:900
void init_immersed_forcing(int lev)
Definition: ERF_InitImmersedForcing.cpp:15
void turbPert_amplitude(const int lev)
Definition: ERF_InitTurbPert.cpp:33
bool use_gravity
Definition: ERF_DataStruct.H:1271
SpongeChoice spongeChoice
Definition: ERF_DataStruct.H:1222

◆ init_phys_bcs()

void ERF::init_phys_bcs ( bool &  rho_read,
bool &  read_prim_theta 
)
private

Initializes data structures in the ERF class that specify which boundary conditions we are implementing on each face of the domain.

This function also maps the selected boundary condition types (e.g. Outflow, Inflow, InflowOutflow, Periodic, Dirichlet, ...) to the specific implementation needed for each variable.

Stores this information in both host and device vectors so it is available for GPU kernels.

21 {
22  auto f = [this,&rho_read,&read_prim_theta] (std::string const& bcid, Orientation ori)
23  {
24  // These are simply defaults for Dirichlet faces -- they should be over-written below
26  m_bc_extdir_vals[BCVars::RhoTheta_bc_comp][ori] = -one; // It is important to set this negative
27  // because the sign is tested on below
28  for (int n = BCVars::RhoKE_bc_comp; n < BCVars::xvel_bc; n++) {
29  m_bc_extdir_vals[n][ori] = zero;
30  }
31 
32  m_bc_extdir_vals[BCVars::xvel_bc][ori] = zero; // default
35 
36  // These are simply defaults for Neumann gradients -- they should be over-written below
39 
48 
52 
53  std::string pp_text = pp_prefix + "." + bcid;
54  ParmParse pp(pp_text);
55 
56  std::string bc_type_in;
57  if (pp.query("type", bc_type_in) <= 0)
58  {
59  pp_text = bcid;
60  pp = ParmParse(pp_text);
61  pp.query("type", bc_type_in);
62  }
63 
64  std::string bc_type = amrex::toLower(bc_type_in);
65 
66  if (bc_type == "symmetry")
67  {
68  // Print() << bcid << " set to symmetry.\n";
70  domain_bc_type[ori] = "Symmetry";
71  }
72  else if ( (ori.coordDir() != 2 && solverChoice.use_real_bcs) || (bc_type == "outflow") )
73  {
74  // Print() << bcid << " set to outflow.\n";
76  domain_bc_type[ori] = "Outflow";
77  }
78  else if (bc_type == "open")
79  {
80  // Print() << bcid << " set to open.\n";
81  AMREX_ASSERT_WITH_MESSAGE((ori.coordDir() != 2), "Open boundary not valid on zlo or zhi!");
83  domain_bc_type[ori] = "Open";
84  }
85  else if (bc_type == "ho_outflow")
86  {
88  domain_bc_type[ori] = "HO_Outflow";
89  }
90 
91  else if (bc_type == "inflow" || bc_type == "inflow_outflow")
92  {
93  if (bc_type == "inflow") {
94  // Print() << bcid << " set to inflow.\n";
96  domain_bc_type[ori] = "Inflow";
97  } else {
98  // Print() << bcid << " set to inflow_outflow.\n";
100  domain_bc_type[ori] = "InflowOutflow";
101  }
102 
103  std::vector<Real> v;
104  if (input_bndry_planes && m_r2d->ingested_velocity()) {
108  } else {
109  // Test for input data file if at xlo face
110  std::string dirichlet_file;
111  auto file_exists = pp.query("dirichlet_file", dirichlet_file);
112  if (file_exists) {
113  pp.query("read_prim_theta", read_prim_theta);
114  init_Dirichlet_bc_data(dirichlet_file);
115  } else {
116  pp.getarr("velocity", v, 0, AMREX_SPACEDIM);
117  m_bc_extdir_vals[BCVars::xvel_bc][ori] = v[0];
118  m_bc_extdir_vals[BCVars::yvel_bc][ori] = v[1];
119  m_bc_extdir_vals[BCVars::zvel_bc][ori] = v[2];
120  }
121  }
122 
123  Real rho_in = zero;
124  if (input_bndry_planes && m_r2d->ingested_density()) {
126  } else {
127  if (!pp.query("density", rho_in)) {
128  amrex::Print() << "Using interior values to set conserved vars" << std::endl;
129  }
130  m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] = rho_in;
131  }
132 
133  bool th_read = (th_bc_data[0].data()!=nullptr);
134  Real theta_in = zero;
135  if (input_bndry_planes && m_r2d->ingested_theta()) {
137  } else if (!th_read) {
138  if (rho_in > 0) {
139  pp.get("theta", theta_in);
140  }
141  m_bc_extdir_vals[BCVars::RhoTheta_bc_comp][ori] = rho_in*theta_in;
142  }
143 
144  // Non-reflecting inflow: prescribe velocity and density but
145  // extrapolate RhoTheta (and hence pressure) from the interior.
146  // This lets upstream-propagating acoustic waves exit the domain
147  // instead of reflecting off the rigid Dirichlet boundary.
148  bool nonreflecting = false;
149  pp.query("nonreflecting", nonreflecting);
150  m_bc_nonreflecting[ori] = nonreflecting;
151 
152  Real scalar_in = zero;
153  if (input_bndry_planes && m_r2d->ingested_scalar()) {
155  } else {
156  if (pp.query("scalar", scalar_in))
157  m_bc_extdir_vals[BCVars::RhoScalar_bc_comp][ori] = rho_in*scalar_in;
158  }
159 
160  if (solverChoice.moisture_type != MoistureType::None) {
161  Real qv_in = zero;
162  if (input_bndry_planes && m_r2d->ingested_q1()) {
164  } else {
165  if (pp.query("qv", qv_in))
166  m_bc_extdir_vals[BCVars::RhoQ1_bc_comp][ori] = rho_in*qv_in;
167  }
168  Real qc_in = zero;
169  if (input_bndry_planes && m_r2d->ingested_q2()) {
171  } else {
172  if (pp.query("qc", qc_in))
173  m_bc_extdir_vals[BCVars::RhoQ2_bc_comp][ori] = rho_in*qc_in;
174  }
175  }
176 
177  Real KE_in = zero;
178  if (input_bndry_planes && m_r2d->ingested_KE()) {
180  } else {
181  if (pp.query("KE", KE_in))
182  m_bc_extdir_vals[BCVars::RhoKE_bc_comp][ori] = rho_in*KE_in;
183  }
184  }
185  else if (bc_type == "noslipwall")
186  {
187  // Print() << bcid <<" set to no-slip wall.\n";
189  domain_bc_type[ori] = "NoSlipWall";
190 
191  std::vector<Real> v;
192 
193  // The values of m_bc_extdir_vals default to zero
194  // But if we find "velocity" in the inputs file, use those values instead.
195  if (pp.queryarr("velocity", v, 0, AMREX_SPACEDIM))
196  {
197  v[ori.coordDir()] = zero;
198  m_bc_extdir_vals[BCVars::xvel_bc][ori] = v[0];
199  m_bc_extdir_vals[BCVars::yvel_bc][ori] = v[1];
200  m_bc_extdir_vals[BCVars::zvel_bc][ori] = v[2];
201  }
202 
203  Real rho_in;
204  rho_read = pp.query("density", rho_in);
205  if (rho_read)
206  {
207  m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] = rho_in;
208  }
209 
210  Real theta_in;
211  if (pp.query("theta", theta_in))
212  {
214  }
215 
216  Real theta_grad_in;
217  if (pp.query("theta_grad", theta_grad_in))
218  {
219  m_bc_neumann_vals[BCVars::RhoTheta_bc_comp][ori] = theta_grad_in;
220  }
221 
222  Real qv_in;
223  if (pp.query("qv", qv_in))
224  {
226  }
227  }
228  else if (bc_type == "slipwall")
229  {
230  // Print() << bcid <<" set to slip wall.\n";
231 
233  domain_bc_type[ori] = "SlipWall";
234 
235  Real rho_in;
236  rho_read = pp.query("density", rho_in);
237  if (rho_read)
238  {
239  m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] = rho_in;
240  }
241 
242  Real theta_in;
243  if (pp.query("theta", theta_in))
244  {
246  }
247 
248  Real rho_grad_in;
249  if (pp.query("density_grad", rho_grad_in))
250  {
251  m_bc_neumann_vals[BCVars::Rho_bc_comp][ori] = rho_grad_in;
252  }
253 
254  Real theta_grad_in;
255  if (pp.query("theta_grad", theta_grad_in))
256  {
257  m_bc_neumann_vals[BCVars::RhoTheta_bc_comp][ori] = theta_grad_in;
258  }
259  }
260  else if (bc_type == "surface_layer")
261  {
263  domain_bc_type[ori] = "surface_layer";
264  }
265  else
266  {
268  }
269 
270  if (geom[0].isPeriodic(ori.coordDir())) {
271  domain_bc_type[ori] = "Periodic";
272  if (phys_bc_type[ori] == ERF_BC::undefined)
273  {
275  } else {
276  Abort("Wrong BC type for periodic boundary");
277  }
278  }
279 
280  if (phys_bc_type[ori] == ERF_BC::undefined)
281  {
283  Print() << "We are using real bc's so don't need to set lateral bc's" << std::endl;
284  } else {
285  Print() << "BC Type specified for face " << bcid << " is " << bc_type_in << std::endl;
286  Abort("This BC type is unknown");
287  }
288  }
289  };
290 
291  f("xlo", Orientation(Direction::x,Orientation::low));
292  f("xhi", Orientation(Direction::x,Orientation::high));
293  f("ylo", Orientation(Direction::y,Orientation::low));
294  f("yhi", Orientation(Direction::y,Orientation::high));
295  f("zlo", Orientation(Direction::z,Orientation::low));
296  f("zhi", Orientation(Direction::z,Orientation::high));
297 }
AMREX_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
void init_Dirichlet_bc_data(const std::string input_file)
Definition: ERF_InitBCs.cpp:735
@ RhoQ6_bc_comp
Definition: ERF_IndexDefines.H:96
@ RhoQ4_bc_comp
Definition: ERF_IndexDefines.H:94
@ RhoQ3_bc_comp
Definition: ERF_IndexDefines.H:93
@ RhoQ5_bc_comp
Definition: ERF_IndexDefines.H:95
Here is the call graph for this function:

◆ init_stuff()

void ERF::init_stuff ( int  lev,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm,
amrex::Vector< amrex::MultiFab > &  lev_new,
amrex::Vector< amrex::MultiFab > &  lev_old,
amrex::MultiFab &  tmp_base_state,
std::unique_ptr< amrex::MultiFab > &  tmp_zphys_nd 
)
private
28 {
29  // ********************************************************************************************
30  // Base state holds r_0, pres_0, pi_0, th_0 (in that order)
31  //
32  // Here is where we set the number of ghost cells for the base state!
33  // ********************************************************************************************
34  int ngb = (solverChoice.terrain_type == TerrainType::EB) ? ComputeGhostCells(solverChoice)+1 : 3;
35  tmp_base_state.define(ba,dm,BaseState::num_comps,ngb);
36  tmp_base_state.setVal(zero);
37 
38  if (solverChoice.terrain_type == TerrainType::MovingFittedMesh) {
39  base_state_new[lev].define(ba,dm,BaseState::num_comps,base_state[lev].nGrowVect());
40  base_state_new[lev].setVal(zero);
41  }
42 
43  // ********************************************************************************************
44  // Allocate terrain arrays
45  // ********************************************************************************************
46 
47  BoxArray ba_nd(ba);
48  ba_nd.surroundingNodes();
49 
50  // NOTE: this is where we actually allocate z_phys_nd -- but here it's called "tmp_zphys_nd"
51  // We need this to be one greater than the ghost cells to handle levels > 0
52 
53  int ngrow = ComputeGhostCells(solverChoice) + 2;
54  tmp_zphys_nd = std::make_unique<MultiFab>(ba_nd,dm,1,IntVect(ngrow,ngrow,ngrow));
55 
56  // Offset z-coordinate for interpolate_1d when plane EB is used.
57  Real z_offset = zero;
58  if (solverChoice.terrain_type == TerrainType::EB) {
59  ParmParse pp_eb2("eb2");
60  std::string geometry;
61  pp_eb2.query("geometry", geometry);
62  if (geometry == "plane") {
63  RealArray plane_point{zero, zero, zero};
64  pp_eb2.query("plane_point", plane_point);
65  z_offset = plane_point[2];
66  }
67  }
68 
69  z_phys_cc[lev] = std::make_unique<MultiFab>(ba,dm,1,2);
70  init_default_zphys(lev, geom[lev], *tmp_zphys_nd, *z_phys_cc[lev], z_offset);
71 
72  if (solverChoice.terrain_type == TerrainType::MovingFittedMesh)
73  {
74  detJ_cc_new[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
75  detJ_cc_src[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
76 
77  ax_src[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(1,0,0)),dm,1,1);
78  ay_src[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,1,0)),dm,1,1);
79  az_src[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)),dm,1,1);
80 
81  z_t_rk[lev] = std::make_unique<MultiFab>( convert(ba, IntVect(0,0,1)), dm, 1, 1 );
82 
83  z_phys_nd_new[lev] = std::make_unique<MultiFab>(ba_nd,dm,1,IntVect(ngrow,ngrow,ngrow));
84  z_phys_nd_src[lev] = std::make_unique<MultiFab>(ba_nd,dm,1,IntVect(ngrow,ngrow,ngrow));
85  z_phys_cc_src[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
86  }
87  else
88  {
89  z_phys_nd_new[lev] = nullptr;
90  detJ_cc_new[lev] = nullptr;
91 
92  z_phys_nd_src[lev] = nullptr;
93  z_phys_cc_src[lev] = nullptr;
94  detJ_cc_src[lev] = nullptr;
95 
96  z_t_rk[lev] = nullptr;
97  }
98 
99  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
100  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
101  {
102  terrain_blanking[lev] = std::make_unique<MultiFab>(ba,dm,1,ngrow);
103  terrain_blanking[lev]->setVal(one);
104  }
105 
106  // We use these area arrays regardless of terrain, EB or none of the above
107  detJ_cc[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
108  ax[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(1,0,0)),dm,1,1);
109  ay[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,1,0)),dm,1,1);
110  az[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)),dm,1,1);
111 
112  detJ_cc[lev]->setVal(one);
113  ax[lev]->setVal(one);
114  ay[lev]->setVal(one);
115  az[lev]->setVal(one);
116 
117  // ********************************************************************************************
118  // Create wall distance array for RANS modeling
119  // ********************************************************************************************
120  if (solverChoice.turbChoice[lev].rans_type != RANSType::None) {
121  walldist[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
122  walldist[lev]->setVal(bogus_large_value);
123  } else {
124  walldist[lev] = nullptr;
125  }
126 
127  // ********************************************************************************************
128  // These are the persistent containers for the old and new data
129  // ********************************************************************************************
130  int ncomp;
131  if (lev > 0) {
132  ncomp = vars_new[lev-1][Vars::cons].nComp();
133  } else {
134  int n_qstate = micro->Get_Qstate_Size();
135  ncomp = NDRY + NSCALARS + n_qstate;
136  }
137 
138  // ********************************************************************************************
139  // The number of ghost cells for density must be 1 greater than that for velocity
140  // so that we can go back in forth between velocity and momentum on all faces
141  // ********************************************************************************************
142  int ngrow_state = ComputeGhostCells(solverChoice) + 1;
143  int ngrow_vels = ComputeGhostCells(solverChoice);
144 
145  // ********************************************************************************************
146  // New solution data containers
147  // ********************************************************************************************
148  if (solverChoice.terrain_type != TerrainType::EB) {
149  lev_new[Vars::cons].define(ba, dm, ncomp, ngrow_state);
150  lev_old[Vars::cons].define(ba, dm, ncomp, ngrow_state);
151  } else {
152  // EB: Define the MultiFabs with the EBFactory
153  lev_new[Vars::cons].define(ba, dm, ncomp, ngrow_state, MFInfo(), EBFactory(lev));
154  lev_old[Vars::cons].define(ba, dm, ncomp, ngrow_state, MFInfo(), EBFactory(lev));
155  }
156 
157  // Initialize all components to zero so we don't need to explicitly set
158  // scalars / moisture variables to zero in the initialization
159  lev_new[Vars::cons].setVal(zero);
160  lev_old[Vars::cons].setVal(zero);
161 
162  lev_new[Vars::xvel].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
163  lev_old[Vars::xvel].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
164 
165  lev_new[Vars::yvel].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
166  lev_old[Vars::yvel].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
167 
168  // Set these to avoid operations on uninitialized data
169  lev_new[Vars::xvel].setVal(bogus_large_value);
170  lev_old[Vars::xvel].setVal(bogus_large_value);
171  lev_new[Vars::yvel].setVal(bogus_large_value);
172  lev_old[Vars::yvel].setVal(bogus_large_value);
173 
174  // Note that we need the ghost cells in the z-direction if we are doing any
175  // kind of domain decomposition in the vertical (at level 0 or above)
176  lev_new[Vars::zvel].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
177  lev_old[Vars::zvel].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
178 
179  gradp[lev][GpVars::gpx].define(convert(ba, IntVect(1,0,0)), dm, 1, 1); gradp[lev][GpVars::gpx].setVal(zero);
180  gradp[lev][GpVars::gpy].define(convert(ba, IntVect(0,1,0)), dm, 1, 1); gradp[lev][GpVars::gpy].setVal(zero);
181  gradp[lev][GpVars::gpz].define(convert(ba, IntVect(0,0,1)), dm, 1, 1); gradp[lev][GpVars::gpz].setVal(zero);
182 
183  if ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) {
184  pp_inc[lev].define(ba, dm, 1, 1);
185  pp_inc[lev].setVal(zero);
186  }
187 
188  // We use this in the fast substepping only
189  if (solverChoice.anelastic[lev] == 0) {
190  lagged_delta_rt[lev].define(ba, dm, 1, 1);
191  lagged_delta_rt[lev].setVal(zero);
192  }
193 
194  // We use these for advecting the slow variables, whether anelastic or compressible
195  avg_xmom[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, 1);
196  avg_ymom[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, 1);
197  avg_zmom[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, 1);
198  avg_xmom[lev].setVal(0); avg_ymom[lev].setVal(0); avg_zmom[lev].setVal(zero);
199 
200  // ********************************************************************************************
201  // These are just used for scratch in the time integrator but we might as well define them here
202  // ********************************************************************************************
203  if (solverChoice.terrain_type != TerrainType::EB) {
204  rU_old[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
205  rU_new[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
206 
207  rV_old[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
208  rV_new[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
209 
210  rW_old[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
211  rW_new[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
212  } else {
213  // EB: Define the MultiFabs with the EBFactory
214  rU_old[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
215  rU_new[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
216 
217  rV_old[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
218  rV_new[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
219 
220  rW_old[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
221  rW_new[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
222  }
223 
224  if (lev > 0) {
225  //xmom_crse_rhs[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, IntVect{0});
226  //ymom_crse_rhs[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, IntVect{0});
227  zmom_crse_rhs[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, IntVect{0});
228  }
229 
230  // We do this here just so they won't be undefined in the initial FillPatch
231  rU_old[lev].setVal(bogus_large_value);
232  rV_old[lev].setVal(bogus_large_value);
233  rW_old[lev].setVal(bogus_large_value);
234  rU_new[lev].setVal(bogus_large_value);
235  rV_new[lev].setVal(bogus_large_value);
236  rW_new[lev].setVal(bogus_large_value);
237 
238  // ********************************************************************************************
239  // These are just time averaged fields for diagnostics
240  // ********************************************************************************************
241 
242  // NOTE: We are not completing a fillpatch call on the time averaged data;
243  // which would copy on intersection and interpolate from coarse.
244  // Therefore, we are restarting the averaging when the ba changes,
245  // this may give poor statistics for dynamic mesh refinement.
246  vel_t_avg[lev] = nullptr;
248  vel_t_avg[lev] = std::make_unique<MultiFab>(ba, dm, 4, 0); // Each vel comp and the mag
249  vel_t_avg[lev]->setVal(zero);
250  t_avg_cnt[lev] = zero;
251  }
252 
253  // ********************************************************************************************
254  // Initialize flux registers whenever we create/re-create a level
255  // ********************************************************************************************
256  if (solverChoice.coupling_type == CouplingType::TwoWay) {
257  if (lev == 0) {
258  advflux_reg[0] = nullptr;
259  } else {
260  int ncomp_reflux = vars_new[0][Vars::cons].nComp();
261  advflux_reg[lev] = new YAFluxRegister(ba , grids[lev-1],
262  dm , dmap[lev-1],
263  geom[lev], geom[lev-1],
264  ref_ratio[lev-1], lev, ncomp_reflux);
265  }
266  }
267 
268  // ********************************************************************************************
269  // Define Theta_prim storage if using surface_layer BC
270  // ********************************************************************************************
271  if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer) {
272  Theta_prim[lev] = std::make_unique<MultiFab>(ba,dm,1,IntVect(ngrow_state,ngrow_state,1));
273  if (solverChoice.moisture_type != MoistureType::None) {
274  Qv_prim[lev] = std::make_unique<MultiFab>(ba,dm,1,IntVect(ngrow_state,ngrow_state,1));
275  Qr_prim[lev] = std::make_unique<MultiFab>(ba,dm,1,IntVect(ngrow_state,ngrow_state,1));
276  } else {
277  Qv_prim[lev] = nullptr;
278  Qr_prim[lev] = nullptr;
279  }
280  } else {
281  Theta_prim[lev] = nullptr;
282  Qv_prim[lev] = nullptr;
283  Qr_prim[lev] = nullptr;
284  }
285 
286  // ********************************************************************************************
287  // Build 1D BA and 2D BA
288  // ********************************************************************************************
289 
290  // NOTE: By design, the compressed BAs have their compressed indices set to 0
291  // MFIters that need more detailed box information should be done over 3D MFs
292 
293  // Build 2D BA
294  BoxList bl2d = ba.boxList();
295  for (auto& b : bl2d) {
296  b.setRange(2,0);
297  }
298  ba2d[lev] = BoxArray(std::move(bl2d));
299 
300  // Build 1D BA
301  BoxList bl1d = ba.boxList();
302  for (auto& b : bl1d) {
303  b.setRange(0,0);
304  b.setRange(1,0);
305  }
306  ba1d[lev] = BoxArray(std::move(bl1d));
307 
308  // ********************************************************************************************
309  // Map factors
310  // ********************************************************************************************
311  mapfac[lev].resize(MapFacType::num);
312  mapfac[lev][MapFacType::m_x] = std::make_unique<MultiFab>( ba2d[lev],dm,1,IntVect(3,3,0));
313  mapfac[lev][MapFacType::u_x] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(1,0,0)),dm,1,IntVect(3,3,0));
314  mapfac[lev][MapFacType::v_x] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(0,1,0)),dm,1,IntVect(3,3,0));
315 
316 #if 0
317  // For now we comment this out to avoid CI failures but we will need to re-enable
318  // this if using non-conformal mappings
320  mapfac[lev][MapFacType::m_y] = std::make_unique<MultiFab>(ba2d[lev],dm,1,IntVect(3,3,0));
321  }
323  mapfac[lev][MapFacType::u_y] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(1,0,0)),dm,1,IntVect(3,3,0));
324  }
326  mapfac[lev][MapFacType::v_y] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(0,1,0)),dm,1,IntVect(3,3,0));
327  }
328 #endif
329 
331  for (int i = 0; i < 3; i++) {
332  mapfac[lev][i]->setVal(myhalf);
333  }
334  for (int i = 3; i < mapfac[lev].size(); i++) {
335  mapfac[lev][i]->setVal(fourth);
336  }
337  } else {
338  for (int i = 0; i < mapfac[lev].size(); i++) {
339  mapfac[lev][i]->setVal(one);
340  }
341  }
342 
343  // ********************************************************************************************
344  // Build WRF data structures
345  // ********************************************************************************************
346  IntVect ng = vars_new[lev][Vars::cons].nGrowVect();
347 
348  if (lev == 0) {
349  wrf_C1H = std::make_unique<MultiFab>(ba1d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
350  wrf_C2H = std::make_unique<MultiFab>(ba1d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
351  wrf_MUB = std::make_unique<MultiFab>(ba2d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
352  }
353 
354  mf_PSFC[lev] = std::make_unique<MultiFab>(ba2d[lev],dm,1,ng);
355 
356  //*********************************************************
357  // Variables for Fitch model for windfarm parametrization
358  //*********************************************************
359 #if defined(ERF_USE_WINDFARM)
360  if (solverChoice.windfarm_type == WindFarmType::Fitch){
361  vars_windfarm[lev].define(ba, dm, 5, ngrow_state); // V, dVabsdt, dudt, dvdt, dTKEdt
362  }
363  if (solverChoice.windfarm_type == WindFarmType::EWP){
364  vars_windfarm[lev].define(ba, dm, 3, ngrow_state); // dudt, dvdt, dTKEdt
365  }
366  if (solverChoice.windfarm_type == WindFarmType::SimpleAD) {
367  vars_windfarm[lev].define(ba, dm, 2, ngrow_state);// dudt, dvdt
368  }
369  if (solverChoice.windfarm_type == WindFarmType::GeneralAD) {
370  vars_windfarm[lev].define(ba, dm, 3, ngrow_state);// dudt, dvdt, dwdt
371  }
372  Nturb[lev].define(ba, dm, 1, ngrow_state); // Number of turbines in a cell
373  SMark[lev].define(ba, dm, 2, 1); // Free stream velocity/source term
374  // sampling marker in a cell - 2 components
375 #endif
376 
377  if(solverChoice.init_type == InitType::HindCast and
379 
380  int ncomp_extra = 2;
381  int nvars = vars_new[lev].size();
382 
383  // Resize all containers
384  forecast_state_1[lev].resize(nvars + 1);
385  forecast_state_2[lev].resize(nvars + 1);
386  forecast_state_interp[lev].resize(nvars + 1);
387 
388  // Define the "normal" components
389  for (int comp = 0; comp < nvars; ++comp) {
390  const MultiFab& src = vars_new[lev][comp];
391  ncomp = src.nComp();
392  ngrow = src.nGrow();
393 
394  forecast_state_1[lev][comp].define(ba, dm, ncomp, ng);
395  forecast_state_2[lev][comp].define(ba, dm, ncomp, ng);
396  forecast_state_interp[lev][comp].define(ba, dm, ncomp, ng);
397  }
398 
399  // Define the "extra" component (last slot)
400  {
401  const MultiFab& src0 = vars_new[lev][0];
402  ngrow = src0.nGrow();
403  int idx = nvars;
404 
405  forecast_state_1[lev][idx].define(ba, dm, ncomp_extra, ngrow);
406  forecast_state_2[lev][idx].define(ba, dm, ncomp_extra, ngrow);
407  forecast_state_interp[lev][idx].define(ba, dm, ncomp_extra, ngrow);
408  }
409  bool regrid_forces_file_read = true;
410  WeatherDataInterpolation(lev, t_new[0],z_phys_nd, regrid_forces_file_read);
411  }
412 
413 
414  if(solverChoice.init_type == InitType::HindCast and
416 
417  {
418  const MultiFab& src = vars_new[lev][0];
419  const amrex::DistributionMapping& dm_hc = src.DistributionMap();
420 
421  surface_state_1[lev].define(ba2d[lev], dm_hc, 2, src.nGrow());
422  surface_state_2[lev].define(ba2d[lev], dm_hc, 2, src.nGrow());
423  surface_state_interp[lev].define(ba2d[lev], dm_hc, 2, src.nGrow());
424 
425  bool regrid_forces_file_read = true;
426  SurfaceDataInterpolation(lev, t_new[0], z_phys_nd, regrid_forces_file_read);
427  }
428 
429 #ifdef ERF_USE_WW3_COUPLING
430  // create a new BoxArray and DistributionMapping for a MultiFab with 1 box
431  BoxArray ba_onegrid(geom[lev].Domain());
432  BoxList bl2d_onegrid = ba_onegrid.boxList();
433  for (auto& b : bl2d_onegrid) { b.setRange(2,b.smallEnd(2)); }
434  BoxArray ba2d_onegrid(std::move(bl2d_onegrid));
435  Vector<int> pmap;
436  pmap.resize(1);
437  pmap[0]=0;
438  DistributionMapping dm_onegrid(ba2d_onegrid);
439  dm_onegrid.define(pmap);
440 
441  Hwave_onegrid[lev] = std::make_unique<MultiFab>(ba2d_onegrid,dm_onegrid,1,IntVect(1,1,0));
442  Lwave_onegrid[lev] = std::make_unique<MultiFab>(ba2d_onegrid,dm_onegrid,1,IntVect(1,1,0));
443 
444  BoxList bl2d_wave = ba.boxList();
445  for (auto& b : bl2d_wave) { b.setRange(2,b.smallEnd(2)); }
446  BoxArray ba2d_wave(std::move(bl2d_wave));
447 
448  Hwave[lev] = std::make_unique<MultiFab>(ba2d_wave,dm,1,IntVect(3,3,0));
449  Lwave[lev] = std::make_unique<MultiFab>(ba2d_wave,dm,1,IntVect(3,3,0));
450 
451  std::cout<<ba_onegrid<<std::endl;
452  std::cout<<ba2d_onegrid<<std::endl;
453  std::cout<<dm_onegrid<<std::endl;
454 #endif
455 
456 
457  //*********************************************************
458  // Radiation heating source terms
459  //*********************************************************
460  if (solverChoice.rad_type != RadiationType::None)
461  {
462  qheating_rates[lev] = std::make_unique<MultiFab>(ba, dm, 2, 0);
463  rad_fluxes[lev] = std::make_unique<MultiFab>(ba, dm, 4, 0);
464  qheating_rates[lev]->setVal(zero);
465  rad_fluxes[lev]->setVal(zero);
466  }
467 
468  //*********************************************************
469  // Turbulent perturbation region initialization
470  //*********************************************************
472  {
473  turbPert.init_tpi_type(lev, solverChoice.pert_type[lev], max_level);
474  turbPert.init_tpi(lev, subdomains[lev], geom[lev].CellSizeArray(),
475  ba, dm, ngrow_state, pp_prefix, refRatio(), max_level);
476  }
477 
478  //
479  // Define the land mask here and set it to all land by default
480  // NOTE: the logic below will BREAK if we have any grids not touching the bottom boundary
481  //
482  {
483  lmask_lev[lev].resize(1);
484  auto ngv = lev_new[Vars::cons].nGrowVect(); ngv[2] = 0;
485  lmask_lev[lev][0] = std::make_unique<iMultiFab>(ba2d[lev],dm,1,ngv);
486  lmask_lev[lev][0]->setVal(solverChoice.is_land[lev]);
487  lmask_lev[lev][0]->FillBoundary(geom[lev].periodicity());
488 
489  land_type_lev[lev].resize(1);
490  land_type_lev[lev][0] = std::make_unique<iMultiFab>(ba2d[lev],dm,1,ngv);
491  land_type_lev[lev][0]->setVal(0);
492  land_type_lev[lev][0]->FillBoundary(geom[lev].periodicity());
493 
494  soil_type_lev[lev].resize(1);
495  soil_type_lev[lev][0] = std::make_unique<iMultiFab>(ba2d[lev],dm,1,ngv);
496  soil_type_lev[lev][0]->setVal(0);
497  soil_type_lev[lev][0]->FillBoundary(geom[lev].periodicity());
498 
499  urb_frac_lev[lev].resize(1);
500  urb_frac_lev[lev][0] = std::make_unique<MultiFab>(ba2d[lev],dm,1,ngv);
501  urb_frac_lev[lev][0]->setVal(one);
502  urb_frac_lev[lev][0]->FillBoundary(geom[lev].periodicity());
503  }
504 
505  // Read in tables needed for windfarm simulations
506  // fill in Nturb multifab - number of turbines in each mesh cell
507  // write out the vtk files for wind turbine location and/or
508  // actuator disks
509  #ifdef ERF_USE_WINDFARM
510  //init_windfarm(lev);
511  #endif
512 
513  if (lev > 0) {
514  fine_mask[lev] = std::make_unique<MultiFab>(grids[lev-1], dmap[lev-1], 1, 0);
515  build_fine_mask(lev, *fine_mask[lev].get());
516  }
517 
518 #ifdef ERF_USE_FFT
519  if ( ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) &&
520  ( (solverChoice.mesh_type == MeshType::ConstantDz) || (solverChoice.mesh_type == MeshType::StretchedDz) ) ) {
521  build_fft_solvers(lev);
522  }
523 #endif
524 }
@ num
Definition: ERF_DataStruct.H:24
#define NDRY
Definition: ERF_IndexDefines.H:13
void init_default_zphys(int, const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc, Real z_offset)
Definition: ERF_TerrainMetrics.cpp:15
std::unique_ptr< amrex::MultiFab > wrf_MUB
Definition: ERF.H:1365
std::unique_ptr< amrex::MultiFab > wrf_C1H
Definition: ERF.H:1363
void build_fine_mask(int lev, amrex::MultiFab &fine_mask)
Definition: ERF_VolWgtSum.cpp:125
static AMREX_FORCE_INLINE int ComputeGhostCells(const SolverChoice &sc)
Definition: ERF.H:1460
amrex::EBFArrayBoxFactory const & EBFactory(int lev) const noexcept
Definition: ERF.H:1739
std::unique_ptr< amrex::MultiFab > wrf_C2H
Definition: ERF.H:1364
@ num_comps
Definition: ERF_IndexDefines.H:78
@ gpz
Definition: ERF_IndexDefines.H:186
@ gpy
Definition: ERF_IndexDefines.H:185
@ gpx
Definition: ERF_IndexDefines.H:184
amrex::Vector< PerturbationType > pert_type
Definition: ERF_DataStruct.H:1382
amrex::Vector< int > is_land
Definition: ERF_DataStruct.H:1233
bool test_mapfactor
Definition: ERF_DataStruct.H:1264
void init_tpi_type(const int lev, const PerturbationType &pert_type, const int max_level)
Definition: ERF_TurbPertStruct.H:28
void init_tpi(const int lev, const amrex::Vector< amrex::BoxArray > &subdomains_lev, const amrex::GpuArray< amrex::Real, 3 > dx, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm, const int ngrow_state, std::string pp_prefix, const amrex::Vector< amrex::IntVect > refRatio, const int max_level)
Definition: ERF_TurbPertStruct.H:52
Here is the call graph for this function:

◆ init_thin_body()

void ERF::init_thin_body ( int  lev,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
899 {
900  //********************************************************************************************
901  // Thin immersed body
902  // *******************************************************************************************
903 #if 0
904  if ((solverChoice.advChoice.zero_xflux.size() > 0) ||
905  (solverChoice.advChoice.zero_yflux.size() > 0) ||
906  (solverChoice.advChoice.zero_zflux.size() > 0))
907  {
908  overset_imask[lev] = std::make_unique<iMultiFab>(ba,dm,1,0);
909  overset_imask[lev]->setVal(1); // == value is unknown (to be solved)
910  }
911 #endif
912 
913  if (solverChoice.advChoice.zero_xflux.size() > 0) {
914  amrex::Print() << "Setting up thin immersed body for "
915  << solverChoice.advChoice.zero_xflux.size() << " xfaces" << std::endl;
916  BoxArray ba_xf(ba);
917  ba_xf.surroundingNodes(0);
918  thin_xforce[lev] = std::make_unique<MultiFab>(ba_xf,dm,1,0);
919  thin_xforce[lev]->setVal(0.0);
920  xflux_imask[lev] = std::make_unique<iMultiFab>(ba_xf,dm,1,0);
921  xflux_imask[lev]->setVal(1);
922  for ( MFIter mfi(*xflux_imask[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
923  {
924  Array4<int> const& imask_arr = xflux_imask[lev]->array(mfi);
925  //Array4<int> const& imask_cell_arr = overset_imask[lev]->array(mfi);
926  Box xbx = mfi.nodaltilebox(0);
927  for (int iv=0; iv < solverChoice.advChoice.zero_xflux.size(); ++iv) {
928  const auto& faceidx = solverChoice.advChoice.zero_xflux[iv];
929  if ((faceidx[0] >= xbx.smallEnd(0)) && (faceidx[0] <= xbx.bigEnd(0)) &&
930  (faceidx[1] >= xbx.smallEnd(1)) && (faceidx[1] <= xbx.bigEnd(1)) &&
931  (faceidx[2] >= xbx.smallEnd(2)) && (faceidx[2] <= xbx.bigEnd(2)))
932  {
933  imask_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
934  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
935  //imask_cell_arr(faceidx[0]-1,faceidx[1],faceidx[2]) = 0;
936  amrex::AllPrint() << " mask xface at " << faceidx << std::endl;
937  }
938  }
939  }
940  } else {
941  thin_xforce[lev] = nullptr;
942  xflux_imask[lev] = nullptr;
943  }
944 
945  if (solverChoice.advChoice.zero_yflux.size() > 0) {
946  amrex::Print() << "Setting up thin immersed body for "
947  << solverChoice.advChoice.zero_yflux.size() << " yfaces" << std::endl;
948  BoxArray ba_yf(ba);
949  ba_yf.surroundingNodes(1);
950  thin_yforce[lev] = std::make_unique<MultiFab>(ba_yf,dm,1,0);
951  thin_yforce[lev]->setVal(0.0);
952  yflux_imask[lev] = std::make_unique<iMultiFab>(ba_yf,dm,1,0);
953  yflux_imask[lev]->setVal(1);
954  for ( MFIter mfi(*yflux_imask[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
955  {
956  Array4<int> const& imask_arr = yflux_imask[lev]->array(mfi);
957  //Array4<int> const& imask_cell_arr = overset_imask[lev]->array(mfi);
958  Box ybx = mfi.nodaltilebox(1);
959  for (int iv=0; iv < solverChoice.advChoice.zero_yflux.size(); ++iv) {
960  const auto& faceidx = solverChoice.advChoice.zero_yflux[iv];
961  if ((faceidx[0] >= ybx.smallEnd(0)) && (faceidx[0] <= ybx.bigEnd(0)) &&
962  (faceidx[1] >= ybx.smallEnd(1)) && (faceidx[1] <= ybx.bigEnd(1)) &&
963  (faceidx[2] >= ybx.smallEnd(2)) && (faceidx[2] <= ybx.bigEnd(2)))
964  {
965  imask_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
966  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
967  //imask_cell_arr(faceidx[0],faceidx[1]-1,faceidx[2]) = 0;
968  amrex::AllPrint() << " mask yface at " << faceidx << std::endl;
969  }
970  }
971  }
972  } else {
973  thin_yforce[lev] = nullptr;
974  yflux_imask[lev] = nullptr;
975  }
976 
977  if (solverChoice.advChoice.zero_zflux.size() > 0) {
978  amrex::Print() << "Setting up thin immersed body for "
979  << solverChoice.advChoice.zero_zflux.size() << " zfaces" << std::endl;
980  BoxArray ba_zf(ba);
981  ba_zf.surroundingNodes(2);
982  thin_zforce[lev] = std::make_unique<MultiFab>(ba_zf,dm,1,0);
983  thin_zforce[lev]->setVal(0.0);
984  zflux_imask[lev] = std::make_unique<iMultiFab>(ba_zf,dm,1,0);
985  zflux_imask[lev]->setVal(1);
986  for ( MFIter mfi(*zflux_imask[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
987  {
988  Array4<int> const& imask_arr = zflux_imask[lev]->array(mfi);
989  //Array4<int> const& imask_cell_arr = overset_imask[lev]->array(mfi);
990  Box zbx = mfi.nodaltilebox(2);
991  for (int iv=0; iv < solverChoice.advChoice.zero_zflux.size(); ++iv) {
992  const auto& faceidx = solverChoice.advChoice.zero_zflux[iv];
993  if ((faceidx[0] >= zbx.smallEnd(0)) && (faceidx[0] <= zbx.bigEnd(0)) &&
994  (faceidx[1] >= zbx.smallEnd(1)) && (faceidx[1] <= zbx.bigEnd(1)) &&
995  (faceidx[2] >= zbx.smallEnd(2)) && (faceidx[2] <= zbx.bigEnd(2)))
996  {
997  imask_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
998  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
999  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]-1) = 0;
1000  amrex::AllPrint() << " mask zface at " << faceidx << std::endl;
1001  }
1002  }
1003  }
1004  } else {
1005  thin_zforce[lev] = nullptr;
1006  zflux_imask[lev] = nullptr;
1007  }
1008 }
amrex::Vector< amrex::IntVect > zero_yflux
Definition: ERF_AdvStruct.H:438
amrex::Vector< amrex::IntVect > zero_xflux
Definition: ERF_AdvStruct.H:437
amrex::Vector< amrex::IntVect > zero_zflux
Definition: ERF_AdvStruct.H:439

◆ init_zphys()

void ERF::init_zphys ( int  lev,
amrex::Real  elapsed_time 
)
687 {
688  // For EB, z_phys_nd was already initialized with the correct z_offset by init_default_zphys.
689  // The terrain-fitting (BTF) done below is irrelevant for a flat EB mesh and would clobber
690  // the offset, so return early here.
691  if (solverChoice.terrain_type == TerrainType::EB) {
692  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
693  micro->Set_dzmin(lev, dzmin);
694  return;
695  }
696 
697  if (solverChoice.init_type != InitType::WRFInput && solverChoice.init_type != InitType::Metgrid)
698  {
699  if (lev > 0) {
700  //
701  // First interpolate from coarser level if there is one
702  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
703  // have been pre-filled - this includes ghost cells both inside and outside
704  // the domain
705  //
706  InterpFromCoarseLevel(*z_phys_nd[lev], z_phys_nd[lev]->nGrowVect(),
707  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
708  *z_phys_nd[lev-1], 0, 0, 1,
709  geom[lev-1], geom[lev],
710  refRatio(lev-1), &node_bilinear_interp,
712  }
713 
714  int ngrow = ComputeGhostCells(solverChoice) + 2;
715  Box bx(surroundingNodes(Geom(lev).Domain())); bx.grow(ngrow);
716  FArrayBox terrain_fab(makeSlab(bx,2,0),1);
717 
718  //
719  // If we are using fitted mesh then we use the surface as defined above
720  // If we are not using fitted mesh but are using z_levels, we still need z_phys (for now)
721  // but we need to use a flat terrain for the mesh itself (the EB data has already been made
722  // from the correct terrain)
723  //
724  if (solverChoice.terrain_type != TerrainType::StaticFittedMesh &&
725  solverChoice.terrain_type != TerrainType::MovingFittedMesh) {
726  terrain_fab.template setVal<RunOn::Device>(zero);
727  } else {
728  //
729  // Fill the values of the terrain height at k=0 only
730  //
731  prob->init_terrain_surface(geom[lev],terrain_fab,elapsed_time);
732  }
733 
734  for (MFIter mfi(*z_phys_nd[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
735  {
736  Box isect = terrain_fab.box() & (*z_phys_nd[lev])[mfi].box();
737  if (!isect.isEmpty()) {
738  (*z_phys_nd[lev])[mfi].template copy<RunOn::Device>(terrain_fab,isect,0,isect,0,1);
739  }
740  }
741 
743 
744  z_phys_nd[lev]->FillBoundary(geom[lev].periodicity());
745 
746  if (lev == 0) {
747  Real zmax = z_phys_nd[0]->max(0,0,false);
748  Real rel_diff = (zmax - zlevels_stag[0][zlevels_stag[0].size()-1]) / zmax;
749  if (rel_diff < Real(1.e-8)) {
750  amrex::Print() << "max of zphys_nd " << zmax << std::endl;
751  amrex::Print() << "max of zlevels " << zlevels_stag[0][zlevels_stag[0].size()-1] << std::endl;
752  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(rel_diff < Real(1.e-8), "Terrain is taller than domain top!");
753  }
754 
755  if (SolverChoice::mesh_type == MeshType::VariableDz)
756  {
757  check_mesh_type(lev);
758  }
759  } // lev == 0
760 
761  } else {
762  // NOTE: If a WRFInput file is NOT provided for a finer level,
763  // we simply interpolate from the coarse. This is necessary
764  // since we average_down the terrain (ERF_MakeNewLevel.cpp L351).
765  // If a WRFInput file IS present, it overwrites the terrain data.
766  if (lev > 0) {
767  //
768  // First interpolate from coarser level if there is one
769  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
770  // have been pre-filled - this includes ghost cells both inside and outside
771  // the domain
772  //
773  InterpFromCoarseLevel(*z_phys_nd[lev], z_phys_nd[lev]->nGrowVect(),
774  z_phys_nd[lev]->nGrowVect(), // DO fill ghost cells outside the domain
775  *z_phys_nd[lev-1], 0, 0, 1,
776  geom[lev-1], geom[lev],
777  refRatio(lev-1), &node_bilinear_interp,
779  }
780  } // init_type
781 
782  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
783  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
784  terrain_blanking[lev]->setVal(one);
785  MultiFab::Subtract(*terrain_blanking[lev], EBFactory(lev).getVolFrac(), 0, 0, 1, ComputeGhostCells(solverChoice) + 2);
786  terrain_blanking[lev]->FillBoundary(geom[lev].periodicity());
787  init_immersed_forcing(lev); // needed for real cases
788  }
789 
790  // Compute the min dz and pass to the micro model
791  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
792  micro->Set_dzmin(lev, dzmin);
793 }
Real get_dzmin_terrain(MultiFab &z_phys_nd)
Definition: ERF_TerrainMetrics.cpp:653
void make_terrain_fitted_coords(int lev, const Geometry &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
Definition: ERF_TerrainMetrics.cpp:47
void check_mesh_type(int lev)
Definition: ERF.cpp:2957
Here is the call graph for this function:

◆ InitData()

void ERF::InitData ( )
544 {
545  BL_PROFILE_VAR("ERF::InitData()", InitData);
546  InitData_pre();
547  InitData_post();
548  BL_PROFILE_VAR_STOP(InitData);
549 }
void InitData_pre()
Definition: ERF.cpp:552
void InitData_post()
Definition: ERF.cpp:576
void InitData()
Definition: ERF.cpp:543

Referenced by main().

Here is the caller graph for this function:

◆ InitData_post()

void ERF::InitData_post ( )
577 {
579  {
580  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(finest_level == 0,
581  "Thin immersed body with refinement not currently supported.");
582  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
583  amrex::Print() << "NOTE: Thin immersed body with non-constant dz has not been tested." << std::endl;
584  }
585  }
586 
587  if (!restart_chkfile.empty()) {
588  restart();
589  }
590 
591  //
592  // Make sure that detJ and z_phys_cc are the average of the data on a finer level if there is one and if two way coupling
593  //
594  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
595  if (solverChoice.coupling_type == CouplingType::TwoWay) {
596  for (int crse_lev = finest_level-1; crse_lev >= 0; crse_lev--) {
597  average_down( *detJ_cc[crse_lev+1], *detJ_cc[crse_lev], 0, 1, refRatio(crse_lev));
598  average_down(*z_phys_cc[crse_lev+1], *z_phys_cc[crse_lev], 0, 1, refRatio(crse_lev));
599  }
600  }
601  for (int crse_lev = finest_level-1; crse_lev >= 0; crse_lev--) {
602  detJ_cc[crse_lev]->FillBoundary(geom[crse_lev].periodicity());
603  z_phys_cc[crse_lev]->FillBoundary(geom[crse_lev].periodicity());
604  }
605  }
606 
607 #ifdef ERF_IMPLICIT_W
608  if ( (SolverChoice::mesh_type == MeshType::VariableDz) &&
610  for (int lev = 0; lev <= finest_level; lev++) {
611  if ( (solverChoice.vert_implicit_fac[lev][0] > 0) ||
612  (solverChoice.vert_implicit_fac[lev][1] > 0) ||
613  (solverChoice.vert_implicit_fac[lev][2] > 0) )
614  {
615  Warning("Doing implicit solve for u, v, and w with terrain at level " << lev << " -- this has not been tested");
616  }
617  }
618  }
619 #endif
620 
621  //
622  // Copy vars_new into vars_old, then use vars_old to fill covered cells in vars_new during AverageDown
623  //
624  if (SolverChoice::terrain_type == TerrainType::EB) {
625  for (int lev = 0; lev <= finest_level; lev++) {
626  int ncomp_cons = vars_new[lev][Vars::cons].nComp();
627  MultiFab::Copy(vars_old[lev][Vars::cons],vars_new[lev][Vars::cons],0,0,ncomp_cons,vars_new[lev][Vars::cons].nGrowVect());
628  }
629  }
630 
631  if (restart_chkfile.empty()) {
632  if (solverChoice.coupling_type == CouplingType::TwoWay) {
633  AverageDown();
634  }
635  }
636 
637 #ifdef ERF_USE_PARTICLES
638  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
639  // Promote the Lagrangian PC to the multi-level ParGDB before init so
640  // per-level addParticles() can use ParticleBoxArray(lev)/DistributionMap(lev).
641  auto* pc_ptr = dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer();
642  AMREX_ALWAYS_ASSERT(pc_ptr != nullptr);
643  pc_ptr->Define(static_cast<amrex::ParGDBBase*>(GetParGDB()));
644 
645  if (restart_chkfile.empty()) {
647  Warning("Tight coupling has not been tested with Lagrangian microphysics");
648  }
649 
650  for (int lev = 0; lev <= finest_level; lev++) {
651  dynamic_cast<LagrangianMicrophysics&>(*micro).initParticles(lev, z_phys_nd[lev]);
652  }
653  }
654  }
655 #endif
656 
657  if (!restart_chkfile.empty()) { // Restart from a checkpoint
658 
659  // Create the physbc objects for {cons, u, v, w, base state}
660  // We fill the additional base state ghost cells just in case we have read the old format
661  for (int lev(0); lev <= finest_level; ++lev) {
662  make_physbcs(lev);
663  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
664  }
665 
667  for (int lev(0); lev <= finest_level; ++lev) {
668  m_forest_drag[lev]->define_drag_field(grids[lev], dmap[lev], geom[lev],
669  z_phys_cc[lev].get(), z_phys_nd[lev].get());
670  }
671  }
672 
673 #ifdef ERF_USE_NETCDF
674  //
675  // Create the needed bdy_data_xlo etc ... since we don't read it in from checkpoint any more
676  // This follows init_from_wrfinput()
677  //
678  bool use_moist = (solverChoice.moisture_type != MoistureType::None);
679  if (solverChoice.use_real_bcs && solverChoice.init_type == InitType::WRFInput) {
680 
681  if ( geom[0].isPeriodic(0) || geom[0].isPeriodic(1) ) {
682  amrex::Error("Cannot set periodic lateral boundary conditions when reading in real boundary values");
683  }
684 
685  bdy_time_interval = read_times_from_wrfbdy(nc_bdy_file,
686  bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
687  start_bdy_time, final_bdy_time);
688 
689  double time_since_start_bdy = t_new[0] + start_time - start_bdy_time;
690  int n_time_old = static_cast<int>(time_since_start_bdy / bdy_time_interval);
691  MultiFab r_hse(base_state[0], make_alias, BaseState::r0_comp, 1);
692  Array<MultiFab*, AMREX_SPACEDIM> area_vec = {ax[0].get(), ay[0].get(), az[0].get()};
693 
694  // Need itime=0 for vertical interpolation
695  if (n_time_old > 0) {
696  int itime = 0;
697  bool is_anelastic = (solverChoice.anelastic[0] == 1);
698  read_and_convert_from_wrfbdy(itime,nc_bdy_file,
699  bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
702  r_hse, area_vec, geom[0], use_moist, domain_bcs_type,
703  real_width, bdy_time_interval, is_anelastic);
704  }
705 
706  int ntimes = std::min(n_time_old+3, static_cast<int>(bdy_data_xlo.size()));
707 
708  for (int itime = n_time_old; itime < ntimes; itime++)
709  {
710  bool is_anelastic = (solverChoice.anelastic[0] == 1);
711  read_and_convert_from_wrfbdy(itime,nc_bdy_file,
712  bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
715  r_hse, area_vec, geom[0], use_moist, domain_bcs_type,
716  real_width, bdy_time_interval, is_anelastic);
717  } // itime
718  } // use_real_bcs
719 
720  if (!nc_low_file.empty())
721  {
722  low_time_interval = read_times_from_wrflow(nc_low_file, low_data_zlo, start_low_time, final_low_time);
723 
724  int lev = 0;
725  sst_lev[lev].resize(low_data_zlo.size());
726  tsk_lev[lev].resize(low_data_zlo.size());
727 
728  double time_since_start_low = t_new[0] + start_time - start_low_time;
729  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
730 
731  int ntimes = std::min(n_time_old+3, static_cast<int>(low_data_zlo.size()));
732 
733  for (int itime = n_time_old; itime < ntimes; itime++)
734  {
735  read_from_wrflow(itime, nc_low_file, geom[lev].Domain(), low_data_zlo);
736 
737  // Need to read PSFC
738  FArrayBox NC_fab_var_file;
739  for (int idx = 0; idx < num_boxes_at_level[lev]; idx++) {
740  int success, use_theta_m;
741  read_from_wrfinput(lev, boxes_at_level[lev][idx], nc_init_file[lev][0],
742  NC_fab_var_file, "PSFC", geom[lev],
743  use_theta_m, success);
744  auto& var_fab = NC_fab_var_file;
745 #ifdef _OPENMP
746 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
747 #endif
748  for ( MFIter mfi(*mf_PSFC[lev], false); mfi.isValid(); ++mfi )
749  {
750  FArrayBox &cur_fab = (*mf_PSFC[lev])[mfi];
751  cur_fab.template copy<RunOn::Device>(var_fab, 0, 0, 1);
752  }
753  var_fab.clear();
754  }
755 
756  update_sst_tsk(itime, geom[lev], ba2d[lev],
757  sst_lev[lev], tsk_lev[lev],
758  m_SurfaceLayer, low_data_zlo,
759  vars_new[lev][Vars::cons], *mf_PSFC[lev],
760  solverChoice.rdOcp, lmask_lev[lev][0], use_moist);
761  } // itime
762  }
763 #endif
764  } // end restart
765 
766 #ifdef ERF_USE_PARTICLES
767  /* If using a Lagrangian microphysics model, its particle container has now been
768  constructed and initialized (calls to micro->Init). So, add its pointer to
769  ERF::particleData and remove its name from list of unallocated particle containers. */
770  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
771  const auto& pc_name( dynamic_cast<LagrangianMicrophysics&>(*micro).getName() );
772  const auto& pc_ptr( dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer() );
773  AMREX_ALWAYS_ASSERT(pc_ptr != nullptr);
774  particleData.pushBack(pc_name, pc_ptr);
775  particleData.getNamesUnalloc().remove(pc_name);
776  }
777 #endif
778 
779  if (input_bndry_planes) {
780  // Read the "time.dat" file to know what data is available
781  m_r2d->read_time_file();
782 
783  // We haven't populated dt yet, set to 0 to ensure assert doesn't crash
784  Real dt_dummy = zero;
785  m_r2d->read_input_files(t_new[0]+start_time,dt_dummy,m_bc_extdir_vals);
786  }
787 
789  {
790  rhotheta_src.resize(max_level+1);
791  for (int lev = 0; lev <= finest_level; lev++) {
792  BoxList bl_src = vars_new[lev][Vars::cons].boxArray().boxList();
793  for (auto& b : bl_src) {
795  {
796  // source is only defined in Z
797  b.setRange(0, 0, 1);
798  b.setRange(1, 0, 1);
799  }
800  }
801  BoxArray ba_src(std::move(bl_src));
802  rhotheta_src[lev] = std::make_unique<MultiFab>(ba_src, vars_new[lev][Vars::cons].DistributionMap(), 1, 0);
803  rhotheta_src[lev]->setVal(0.);
804  prob->update_rhotheta_sources(t_new[0],
805  rhotheta_src[lev].get(),
806  geom[lev], z_phys_cc[lev]);
807  }
808  }
809 
811  {
812  h_u_geos.resize(max_level+1, Vector<Real>(0));
813  d_u_geos.resize(max_level+1, Gpu::DeviceVector<Real>(0));
814  h_v_geos.resize(max_level+1, Vector<Real>(0));
815  d_v_geos.resize(max_level+1, Gpu::DeviceVector<Real>(0));
816  for (int lev = 0; lev <= finest_level; lev++) {
817  const int domlen = geom[lev].Domain().length(2);
818  h_u_geos[lev].resize(domlen, 0.0_rt);
819  d_u_geos[lev].resize(domlen, 0.0_rt);
820  h_v_geos[lev].resize(domlen, 0.0_rt);
821  d_v_geos[lev].resize(domlen, 0.0_rt);
823  prob->update_geostrophic_profile(t_new[0],
824  h_u_geos[lev], d_u_geos[lev],
825  h_v_geos[lev], d_v_geos[lev],
826  geom[lev], z_phys_cc[lev]);
827  } else {
828  if (SolverChoice::mesh_type == MeshType::VariableDz) {
829  amrex::Print() << "Note: 1-D geostrophic wind profile input is not defined for real terrain" << std::endl;
830  }
832  h_u_geos[lev], d_u_geos[lev],
833  h_v_geos[lev], d_v_geos[lev],
834  geom[lev],
835  zlevels_stag[0]);
836  }
837  }
838  }
839 
841  {
842  rhoqt_src.resize(max_level+1);
843  for (int lev = 0; lev <= finest_level; lev++) {
844  BoxList bl_src = vars_new[lev][Vars::cons].boxArray().boxList();
845  for (auto& b : bl_src) {
847  {
848  // source is only defined in Z
849  b.setRange(0, 0, 1);
850  b.setRange(1, 0, 1);
851  }
852  }
853  BoxArray ba_src(std::move(bl_src));
854  rhoqt_src[lev] = std::make_unique<MultiFab>(ba_src, vars_new[lev][Vars::cons].DistributionMap(), 1, 0);
855  rhoqt_src[lev]->setVal(0.);
856  prob->update_rhoqt_sources(t_new[0],
857  rhoqt_src[lev].get(),
858  geom[lev], z_phys_cc[lev]);
859  }
860  }
861 
863  {
864  h_w_subsid.resize(max_level+1, Vector<Real>(0));
865  d_w_subsid.resize(max_level+1, Gpu::DeviceVector<Real>(0));
866  for (int lev = 0; lev <= finest_level; lev++) {
867  const int domlen = geom[lev].Domain().length(2) + 1; // lives on z-faces
868  h_w_subsid[lev].resize(domlen, 0.0_rt);
869  d_w_subsid[lev].resize(domlen, 0.0_rt);
870  prob->update_w_subsidence(t_new[0],
871  h_w_subsid[lev], d_w_subsid[lev], base_state[lev],
872  geom[lev], z_phys_nd[lev]);
873  }
874  }
875 
878  {
879  for (int lev = 0; lev <= finest_level; lev++) {
881  }
882  if (solverChoice.init_type == InitType::Input_Sounding)
883  {
884  // Overwrite ubar, vbar, and thetabar with input profiles;
885  // wbar is assumed to be zero Note: the tau coefficient set by
886  // prob->erf_init_rayleigh() is still used
887  bool restarting = (!restart_chkfile.empty());
888  setRayleighRefFromSounding(restarting);
889  }
890  }
891 
892  // Read in sponge data from input file
893  if(solverChoice.spongeChoice.sponge_type == SpongeType::Input_Sponge)
894  {
895  initSponge();
896  bool restarting = (!restart_chkfile.empty());
897  setSpongeRefFromSounding(restarting);
898  }
899 
901  if (is_it_time_for_action(istep[0], t_new[0], dt[0], pert_interval, -one)) {
902  turbPert.debug(t_new[0]);
903  }
904  }
905 
906  // We only write the file at level 0 for now
908  {
909  // Create the WriteBndryPlanes object so we can handle writing of boundary plane data
910  m_w2d = std::make_unique<WriteBndryPlanes>(grids,geom);
911 
912  Real tot_time = t_new[0]+start_time;
913  if (tot_time >= bndry_output_planes_start_time) {
914  bool is_moist = (micro->Get_Qstate_Moist_Size() > 0);
915  m_w2d->write_planes(0, tot_time, vars_new, is_moist);
916  }
917  }
918 
919  // Fill boundary conditions in vars_new
920  for (int lev = 0; lev <= finest_level; ++lev)
921  {
922  auto& lev_new = vars_new[lev];
923 
924  // ***************************************************************************
925  // Physical bc's at domain boundary
926  // ***************************************************************************
927  IntVect ngvect_cons = vars_new[lev][Vars::cons].nGrowVect();
928  IntVect ngvect_vels = vars_new[lev][Vars::xvel].nGrowVect();
929 
930  int ncomp_cons = lev_new[Vars::cons].nComp();
931  bool do_fb = true;
932 
933 #ifdef ERF_USE_NETCDF
934  if (solverChoice.use_real_bcs && (lev==0)) {
935  int icomp_cons = 0;
936  bool cons_only = false;
937  Vector<MultiFab*> mfs_vec = {&lev_new[Vars::cons],&lev_new[Vars::xvel],
938  &lev_new[Vars::yvel],&lev_new[Vars::zvel]};
939  fill_from_realbdy(mfs_vec,t_new[lev],cons_only,icomp_cons,
940  ncomp_cons,ngvect_cons,ngvect_vels);
941  do_fb = false;
942  }
943 #endif
944 
945  (*physbcs_cons[lev])(lev_new[Vars::cons],lev_new[Vars::xvel],lev_new[Vars::yvel],0,ncomp_cons,
946  ngvect_cons,t_new[lev],BCVars::cons_bc,do_fb);
947  ( *physbcs_u[lev])(lev_new[Vars::xvel],lev_new[Vars::xvel],lev_new[Vars::yvel],
948  ngvect_vels,t_new[lev],BCVars::xvel_bc,do_fb);
949  ( *physbcs_v[lev])(lev_new[Vars::yvel],lev_new[Vars::xvel],lev_new[Vars::yvel],
950  ngvect_vels,t_new[lev],BCVars::yvel_bc,do_fb);
951  ( *physbcs_w[lev])(lev_new[Vars::zvel],lev_new[Vars::xvel],lev_new[Vars::yvel],
952  ngvect_vels,t_new[lev],BCVars::zvel_bc,do_fb);
953  }
954 
955  //
956  // If we are starting from scratch, we have the option to project the initial velocity field
957  // regardless of how we initialized. Note that project_initial_velocity operates on vars_new.
958  // pp_inc is used as scratch space here; we zero it out after the projection
959  //
960  if (restart_chkfile == "")
961  {
962  for (int lev = 0; lev <= finest_level; ++lev)
963  {
964  if (solverChoice.project_initial_velocity[lev] == 1) {
965  Real dummy_dt = one;
966  if (verbose > 0) {
967  amrex::Print() << "Projecting initial velocity field at level " << lev << std::endl;
968  }
969 
970  project_initial_velocity(lev, t_new[lev], dummy_dt);
971 
972  pp_inc[lev].setVal(0.);
973  gradp[lev][GpVars::gpx].setVal(0.);
974  gradp[lev][GpVars::gpy].setVal(0.);
975  gradp[lev][GpVars::gpz].setVal(0.);
976  } // project
977  } // lev
978  }
979 
980  // Copy from new into old just in case (after filling boundary conditions and possibly projecting)
981  for (int lev = 0; lev <= finest_level; ++lev)
982  {
983  int nc = vars_new[lev][Vars::cons].nComp();
984 
985  MultiFab::Copy(vars_old[lev][Vars::cons],vars_new[lev][Vars::cons],0,0,nc,vars_new[lev][Vars::cons].nGrowVect());
986  MultiFab::Copy(vars_old[lev][Vars::xvel],vars_new[lev][Vars::xvel],0,0, 1,vars_new[lev][Vars::xvel].nGrowVect());
987  MultiFab::Copy(vars_old[lev][Vars::yvel],vars_new[lev][Vars::yvel],0,0, 1,vars_new[lev][Vars::yvel].nGrowVect());
988  MultiFab::Copy(vars_old[lev][Vars::zvel],vars_new[lev][Vars::zvel],0,0, 1,vars_new[lev][Vars::zvel].nGrowVect());
989  }
990 
991  // Compute the minimum dz in the domain at each level (to be used for setting the timestep)
992  dz_min.resize(max_level+1);
993  for (int lev = 0; lev <= finest_level; ++lev)
994  {
995  dz_min[lev] = geom[lev].CellSize(2);
996  if ( SolverChoice::mesh_type != MeshType::ConstantDz ) {
997  dz_min[lev] *= (*detJ_cc[lev]).min(0);
998  }
999  }
1000 
1001 
1002  // We don't need to recompute dt[lev] on restart because we read it in from the checkpoint file.
1003  if (restart_chkfile.empty()) {
1004  ComputeDt();
1005  }
1006 
1007  // Check the viscous limit
1011  Real delta = std::min({geom[finest_level].CellSize(0),
1012  geom[finest_level].CellSize(1),
1013  dz_min[finest_level]});
1014  if (dc.dynamic_viscosity == 0) {
1015  Print() << "Note: Molecular diffusion specified but dynamic_viscosity has not been specified" << std::endl;
1016  } else {
1017  Real nu = dc.dynamic_viscosity / dc.rho0_trans;
1018  Real viscous_limit = two * delta*delta / nu;
1019  Print() << "smallest grid spacing at level " << finest_level << " = " << delta << std::endl;
1020  Print() << "dt at level " << finest_level << " = " << dt[finest_level] << std::endl;
1021  Print() << "Viscous CFL is " << dt[finest_level] / viscous_limit << std::endl;
1022  if (fixed_dt[finest_level] >= viscous_limit) {
1023  Warning("Specified fixed_dt is above the viscous limit");
1024  } else if (dt[finest_level] >= viscous_limit) {
1025  Warning("Adaptive dt based on convective CFL only is above the viscous limit");
1026  }
1027  }
1028  }
1029 
1030  // Fill ghost cells/faces
1031  for (int lev = 0; lev <= finest_level; ++lev)
1032  {
1033  if (lev > 0 && cf_width >= 0) {
1035  }
1036 
1037  auto& lev_new = vars_new[lev];
1038 
1039  //
1040  // Fill boundary conditions -- not sure why we need this here
1041  //
1042  bool fillset = false;
1043  if (lev == 0) {
1044  FillPatchCrseLevel(lev, t_new[lev],
1045  {&lev_new[Vars::cons],&lev_new[Vars::xvel],&lev_new[Vars::yvel],&lev_new[Vars::zvel]});
1046  } else {
1047  FillPatchFineLevel(lev, t_new[lev],
1048  {&lev_new[Vars::cons],&lev_new[Vars::xvel],&lev_new[Vars::yvel],&lev_new[Vars::zvel]},
1049  {&lev_new[Vars::cons],&rU_new[lev],&rV_new[lev],&rW_new[lev]},
1050  base_state[lev], base_state[lev],
1051  fillset);
1052  }
1053 
1054  //
1055  // We do this here to make sure level (lev-1) boundary conditions are filled
1056  // before we interpolate to level (lev) ghost cells
1057  //
1058  if (lev < finest_level) {
1059  auto& lev_old = vars_old[lev];
1060  MultiFab::Copy(lev_old[Vars::cons],lev_new[Vars::cons],0,0,lev_old[Vars::cons].nComp(),lev_old[Vars::cons].nGrowVect());
1061  MultiFab::Copy(lev_old[Vars::xvel],lev_new[Vars::xvel],0,0,lev_old[Vars::xvel].nComp(),lev_old[Vars::xvel].nGrowVect());
1062  MultiFab::Copy(lev_old[Vars::yvel],lev_new[Vars::yvel],0,0,lev_old[Vars::yvel].nComp(),lev_old[Vars::yvel].nGrowVect());
1063  MultiFab::Copy(lev_old[Vars::zvel],lev_new[Vars::zvel],0,0,lev_old[Vars::zvel].nComp(),lev_old[Vars::zvel].nGrowVect());
1064  }
1065 
1066  //
1067  // We fill the ghost cell values of the base state in case it wasn't done in the initialization
1068  //
1069  base_state[lev].FillBoundary(geom[lev].periodicity());
1070 
1071  // For moving terrain only
1072  if (solverChoice.terrain_type == TerrainType::MovingFittedMesh) {
1073  MultiFab::Copy(base_state_new[lev],base_state[lev],0,0,BaseState::num_comps,base_state[lev].nGrowVect());
1074  base_state_new[lev].FillBoundary(geom[lev].periodicity());
1075  }
1076 
1077  }
1078 
1079  // If lev > 0, we need to fill bc's by interpolation from coarser grid
1080  for (int lev = 1; lev <= finest_level; ++lev)
1081  {
1082  Interp2DArrays(lev,ba2d[lev],dmap[lev]);
1083  } // lev
1084 
1085 #ifdef ERF_USE_WW3_COUPLING
1086  int my_lev = 0;
1087  amrex::Print() << " About to call send_to_ww3 from ERF.cpp" << std::endl;
1088  send_to_ww3(my_lev);
1089  amrex::Print() << " About to call read_waves from ERF.cpp" << std::endl;
1090  read_waves(my_lev);
1091  // send_to_ww3(my_lev);
1092 #endif
1093 
1094  // Create wall distance field for RANS model
1095  for (int lev = 0; lev <= finest_level; lev++) {
1096  if (solverChoice.turbChoice[lev].rans_type != RANSType::None) {
1097  // Handle bottom boundary
1098  poisson_wall_dist(lev);
1099 
1100  // Correct the wall distance for immersed bodies
1106  geom[lev],
1107  z_phys_cc[lev]);
1108  }
1109  }
1110  }
1111 
1112  // Configure SurfaceLayer params if used
1113  // NOTE: we must set up the MOST routine after calling FillPatch
1114  // in order to have lateral ghost cells filled (MOST + terrain interp).
1115  if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer)
1116  {
1118  (solverChoice.turbChoice[0].les_type != LESType::None) ||
1119  (solverChoice.turbChoice[0].rans_type != RANSType::None) ||
1120  (solverChoice.turbChoice[0].pbl_type != PBLType::None) );
1121  AMREX_ALWAYS_ASSERT(has_diff);
1122 
1123  bool rotate = solverChoice.use_rotate_surface_flux;
1124  if (rotate) {
1125  Print() << "Using surface layer model with stress rotations" << std::endl;
1126  }
1127 
1128  //
1129  // This constructor will make the SurfaceLayer object but not allocate the arrays at each level.
1130  //
1131  // Build vector of eb pointers for all levels
1132  amrex::Vector<const eb_*> eb_ptrs;
1133  eb_ptrs.resize(finest_level + 1, nullptr);
1134  if (solverChoice.terrain_type == TerrainType::EB) {
1135  for (int lev = 0; lev <= finest_level; lev++) {
1136  eb_ptrs[lev] = eb[lev] ? eb[lev].get() : nullptr;
1137  }
1138  }
1139 
1140  m_SurfaceLayer = std::make_unique<SurfaceLayer>(geom, rotate, pp_prefix, Qv_prim,
1141  z_phys_nd,
1144  solverChoice.turbChoice[finest_level],
1145 #ifdef ERF_USE_NETCDF
1146  start_low_time, final_low_time, low_time_interval,
1147 #else
1148  zero, zero, zero,
1149 #endif
1150  eb_ptrs);
1151  // This call will allocate the arrays at each level. If we regrid later, either changing
1152  // the number of levels or just the grids at each existing level, we will call an update routine
1153  // to redefine the internal arrays in m_SurfaceLayer.
1154  for (int lev = 0; lev <= finest_level; lev++)
1155  {
1156  Vector<MultiFab*> mfv_old = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::xvel],
1157  &vars_old[lev][Vars::yvel], &vars_old[lev][Vars::zvel]};
1158  m_SurfaceLayer->make_SurfaceLayer_at_level(lev,finest_level+1,
1159  mfv_old, Theta_prim[lev], Qv_prim[lev],
1160  Qr_prim[lev], z_phys_nd[lev],
1161  Hwave[lev].get(),Lwave[lev].get(),eddyDiffs_lev[lev].get(),
1163  sst_lev[lev], tsk_lev[lev], lmask_lev[lev]);
1164  }
1165 
1166  // If initializing from an input_sounding, make sure the surface layer
1167  // is using the same surface conditions
1168  if (solverChoice.init_type == InitType::Input_Sounding) {
1171  for (int lev = 0; lev <= finest_level; lev++) {
1172  m_SurfaceLayer->set_t_surf(lev, theta0);
1173  m_SurfaceLayer->set_q_surf(lev, qv0);
1174  }
1175  }
1176 
1177  if (restart_chkfile != "") {
1178  // Update surface fields if needed (and available)
1180  }
1181 
1182  // We now configure ABLMost params here so that we can print the averages at t=0
1183  // Note we don't fill ghost cells here because this is just for diagnostics
1184  for (int lev = 0; lev <= finest_level; ++lev)
1185  {
1186  IntVect ng = Theta_prim[lev]->nGrowVect();
1187 
1188  MultiFab::Copy( *Theta_prim[lev], vars_new[lev][Vars::cons], RhoTheta_comp, 0, 1, ng);
1189  MultiFab::Divide(*Theta_prim[lev], vars_new[lev][Vars::cons], Rho_comp, 0, 1, ng);
1190 
1191  if (solverChoice.moisture_type != MoistureType::None) {
1192  ng = Qv_prim[lev]->nGrowVect();
1193 
1194  MultiFab::Copy( *Qv_prim[lev], vars_new[lev][Vars::cons], RhoQ1_comp, 0, 1, ng);
1195  MultiFab::Divide(*Qv_prim[lev], vars_new[lev][Vars::cons], Rho_comp, 0, 1, ng);
1196 
1197  int rhoqr_comp = solverChoice.moisture_indices.qr;
1198  if (rhoqr_comp > -1) {
1199  MultiFab::Copy( *Qr_prim[lev], vars_new[lev][Vars::cons], rhoqr_comp, 0, 1, ng);
1200  MultiFab::Divide(*Qr_prim[lev], vars_new[lev][Vars::cons], Rho_comp, 0, 1, ng);
1201  } else {
1202  Qr_prim[lev]->setVal(0.0);
1203  }
1204  }
1205  m_SurfaceLayer->update_mac_ptrs(lev, vars_new, Theta_prim, Qv_prim, Qr_prim);
1206 
1207  if (restart_chkfile == "") {
1208  // Only do this if starting from scratch; if restarting, then
1209  // we don't want to call update_fluxes multiple times because
1210  // it will change u* and theta* from their previous values
1211  m_SurfaceLayer->update_pblh(lev, vars_new, z_phys_cc[lev].get(),
1213 #ifdef ERF_USE_NETCDF
1214  Real elapsed_time_since_start_low = t_new[lev] + (start_time - start_low_time);
1215 #else
1216  Real elapsed_time_since_start_low = t_new[lev] + start_time;
1217 #endif
1218  m_SurfaceLayer->update_fluxes(lev, t_new[lev], elapsed_time_since_start_low,
1219  vars_new[lev][Vars::cons],
1220  z_phys_nd[lev],
1221  walldist[lev]);
1222 
1223  // Initialize tke(x,y,z) as a function of u*(x,y)
1224  if (solverChoice.turbChoice[lev].init_tke_from_ustar) {
1225  Real qkefac = one;
1226  if (solverChoice.turbChoice[lev].pbl_type == PBLType::MYNN25 ||
1227  solverChoice.turbChoice[lev].pbl_type == PBLType::MYNNEDMF)
1228  {
1229  // https://github.com/NCAR/MYNN-EDMF/blob/90f36c25259ec1960b24325f5b29ac7c5adeac73/module_bl_mynnedmf.F90#L1325-L1333
1230  const Real B1 = solverChoice.turbChoice[lev].pbl_mynn.B1;
1231  qkefac = Real(1.5) * std::pow(B1, two/three);
1232  }
1233  m_SurfaceLayer->init_tke_from_ustar(lev, vars_new[lev][Vars::cons], z_phys_nd[lev], qkefac);
1234  }
1235  }
1236  }
1237  } // end if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer)
1238 
1239  // Update micro vars and finish moisture model initializations before first plot file
1240  if (solverChoice.moisture_type != MoistureType::None) {
1241  for (int lev = 0; lev <= finest_level; ++lev) {
1242  micro->Update_Micro_Vars_Lev(lev, vars_new[lev][Vars::cons]);
1243  micro->FinishInit(lev, vars_new[lev][Vars::cons], z_phys_nd);
1244  }
1245  }
1246 
1247  // Fill time averaged velocities before first plot file
1248  if (solverChoice.time_avg_vel) {
1249  for (int lev = 0; lev <= finest_level; ++lev) {
1250  Time_Avg_Vel_atCC(dt[lev], t_avg_cnt[lev], vel_t_avg[lev].get(),
1251  vars_new[lev][Vars::xvel],
1252  vars_new[lev][Vars::yvel],
1253  vars_new[lev][Vars::zvel]);
1254  }
1255  }
1256 
1257 #ifdef ERF_USE_PARTICLES
1258  // Redistribute particles so the container has valid data at all AMR levels
1259  // before the initial plotfile write
1260  if (finest_level > 0) {
1261  particleData.Redistribute(z_phys_nd);
1262  }
1263 #endif
1264 
1265  // Print max values of lateral gradients of base state pressure at level 0
1266  if (verbose > 0) {
1267  for (int lev = 0; lev <= finest_level; ++lev) {
1268  if (lev == 0) {
1270  }
1271  }
1272  }
1273 
1274  // check for additional plotting variables that are available after particle containers
1275  // are setup.
1276  const std::string& pv3d_1 = "plot_vars_1" ; appendPlotVariables(pv3d_1,plot3d_var_names_1);
1277  const std::string& pv3d_2 = "plot_vars_2" ; appendPlotVariables(pv3d_2,plot3d_var_names_2);
1278  const std::string& pv2d_1 = "plot2d_vars_1"; appendPlotVariables(pv2d_1,plot2d_var_names_1);
1279  const std::string& pv2d_2 = "plot2d_vars_2"; appendPlotVariables(pv2d_2,plot2d_var_names_2);
1280 
1281  if ( restart_chkfile.empty() && (m_check_int > 0 || m_check_per > zero) )
1282  {
1286  }
1287 
1288  if ( (restart_chkfile.empty()) ||
1289  (!restart_chkfile.empty() && plot_file_on_restart) )
1290  {
1291  if (m_plot3d_int_1 > 0 || m_plot3d_per_1 > zero)
1292  {
1296  }
1297  if (m_plot3d_int_2 > 0 || m_plot3d_per_2 > zero)
1298  {
1302  }
1303  if (m_plot2d_int_1 > 0 || m_plot2d_per_1 > zero)
1304  {
1308  }
1309  if (m_plot2d_int_2 > 0 || m_plot2d_per_2 > zero)
1310  {
1314  }
1315  for (int i = 0; i < m_subvol_int.size(); i++) {
1316  if (m_subvol_int[i] > 0 || m_subvol_per[i] > zero) {
1318  last_subvol_step[i] = istep[0];
1319  if (m_subvol_per[i] > zero) {last_subvol_time[i] += m_subvol_per[i];}
1320  }
1321  }
1322  }
1323 
1324  // Set these up here because we need to know which MPI rank "cell" is on...
1325  ParmParse pp("erf");
1326  if (pp.contains("data_log"))
1327  {
1328  int num_datalogs = pp.countval("data_log");
1329  datalog.resize(num_datalogs);
1330  datalogname.resize(num_datalogs);
1331  pp.queryarr("data_log",datalogname,0,num_datalogs);
1332  for (int i = 0; i < num_datalogs; i++) {
1334  }
1335  }
1336 
1337  if (pp.contains("der_data_log"))
1338  {
1339  int num_der_datalogs = pp.countval("der_data_log");
1340  der_datalog.resize(num_der_datalogs);
1341  der_datalogname.resize(num_der_datalogs);
1342  pp.queryarr("der_data_log",der_datalogname,0,num_der_datalogs);
1343  for (int i = 0; i < num_der_datalogs; i++) {
1345  }
1346  }
1347 
1348  if (pp.contains("energy_data_log"))
1349  {
1350  int num_energy_datalogs = pp.countval("energy_data_log");
1351  tot_e_datalog.resize(num_energy_datalogs);
1352  tot_e_datalogname.resize(num_energy_datalogs);
1353  pp.queryarr("energy_data_log",tot_e_datalogname,0,num_energy_datalogs);
1354  for (int i = 0; i < num_energy_datalogs; i++) {
1356  }
1357  }
1358 
1359  if (solverChoice.rad_type != RadiationType::None)
1360  {
1361  // Create data log for radiation model if requested
1362  rad[0]->setupDataLog();
1363  }
1364 
1365 
1366  if (restart_chkfile.empty() && profile_int > 0) {
1367  if (destag_profiles) {
1368  // all variables cell-centered
1370  } else {
1371  // some variables staggered
1373  }
1374  }
1375 
1376  if (pp.contains("sample_point_log") && pp.contains("sample_point"))
1377  {
1378  int lev = 0;
1379 
1380  int num_samplepts = pp.countval("sample_point") / AMREX_SPACEDIM;
1381  if (num_samplepts > 0) {
1382  Vector<int> index; index.resize(num_samplepts*AMREX_SPACEDIM);
1383  samplepoint.resize(num_samplepts);
1384 
1385  pp.queryarr("sample_point",index,0,num_samplepts*AMREX_SPACEDIM);
1386  for (int i = 0; i < num_samplepts; i++) {
1387  IntVect iv(index[AMREX_SPACEDIM*i+0],index[AMREX_SPACEDIM*i+1],index[AMREX_SPACEDIM*i+2]);
1388  samplepoint[i] = iv;
1389  }
1390  }
1391 
1392  int num_sampleptlogs = pp.countval("sample_point_log");
1393  AMREX_ALWAYS_ASSERT(num_sampleptlogs == num_samplepts);
1394  if (num_sampleptlogs > 0) {
1395  sampleptlog.resize(num_sampleptlogs);
1396  sampleptlogname.resize(num_sampleptlogs);
1397  pp.queryarr("sample_point_log",sampleptlogname,0,num_sampleptlogs);
1398 
1399  for (int i = 0; i < num_sampleptlogs; i++) {
1401  }
1402  }
1403 
1404  }
1405 
1406  bool has_sample_line = pp.contains("sample_line");
1407  bool has_sample_line_real = pp.contains("sample_line_real");
1408  if (has_sample_line && has_sample_line_real) {
1409  Abort("Specify only one of erf.sample_line or erf.sample_line_real");
1410  }
1411 
1412  if (pp.contains("sample_line_log") && (has_sample_line || has_sample_line_real))
1413  {
1414  int lev = 0;
1415 
1416  int num_samplelines = 0;
1417  if (has_sample_line) {
1418  num_samplelines = pp.countval("sample_line") / AMREX_SPACEDIM;
1419  if (num_samplelines > 0) {
1420  Vector<int> index; index.resize(num_samplelines*AMREX_SPACEDIM);
1421  sampleline.resize(num_samplelines);
1422 
1423  pp.queryarr("sample_line",index,0,num_samplelines*AMREX_SPACEDIM);
1424  for (int i = 0; i < num_samplelines; i++) {
1425  IntVect iv(index[AMREX_SPACEDIM*i+0],index[AMREX_SPACEDIM*i+1],index[AMREX_SPACEDIM*i+2]);
1426  sampleline[i] = iv;
1427  }
1428  }
1429  } else {
1430  int num_real_vals = pp.countval("sample_line_real");
1431  if (num_real_vals % AMREX_SPACEDIM != 0) {
1432  Abort("erf.sample_line_real must be specified as (x,y,z) triples");
1433  }
1434 
1435  num_samplelines = num_real_vals / AMREX_SPACEDIM;
1436  if (num_samplelines > 0) {
1437  Vector<Real> location; location.resize(num_real_vals);
1438  sampleline.resize(num_samplelines);
1439 
1440  pp.queryarr("sample_line_real",location,0,num_real_vals);
1441 
1442  const Box& domain = geom[lev].Domain();
1443  const auto* prob_lo = geom[lev].ProbLo();
1444  const auto* prob_hi = geom[lev].ProbHi();
1445  const auto* dx = geom[lev].CellSize();
1446 
1447  for (int i = 0; i < num_samplelines; i++) {
1448  Real xloc = location[AMREX_SPACEDIM*i+0];
1449  Real yloc = location[AMREX_SPACEDIM*i+1];
1450  Real zloc = location[AMREX_SPACEDIM*i+2];
1451 
1452  if (xloc < prob_lo[0] || xloc > prob_hi[0] ||
1453  yloc < prob_lo[1] || yloc > prob_hi[1] ||
1454  zloc < prob_lo[2] || zloc > prob_hi[2]) {
1455  Abort("erf.sample_line_real must lie within the level-0 domain");
1456  }
1457 
1458  int i_cell = domain.smallEnd(0) + static_cast<int>(std::floor((xloc - prob_lo[0]) / dx[0]));
1459  int j_cell = domain.smallEnd(1) + static_cast<int>(std::floor((yloc - prob_lo[1]) / dx[1]));
1460  int k_cell = domain.smallEnd(2) + static_cast<int>(std::floor((zloc - prob_lo[2]) / dx[2]));
1461 
1462  i_cell = std::min(i_cell, domain.bigEnd(0));
1463  j_cell = std::min(j_cell, domain.bigEnd(1));
1464  k_cell = std::min(k_cell, domain.bigEnd(2));
1465 
1466  sampleline[i] = IntVect(i_cell, j_cell, k_cell);
1467  }
1468  }
1469  }
1470 
1471  int num_samplelinelogs = pp.countval("sample_line_log");
1472  AMREX_ALWAYS_ASSERT(num_samplelinelogs == num_samplelines);
1473  if (num_samplelinelogs > 0) {
1474  samplelinelog.resize(num_samplelinelogs);
1475  samplelinelogname.resize(num_samplelinelogs);
1476  pp.queryarr("sample_line_log",samplelinelogname,0,num_samplelinelogs);
1477 
1478  for (int i = 0; i < num_samplelinelogs; i++) {
1480  }
1481  }
1482 
1483  }
1484 
1489  }
1490 
1491  // Create object to do line and plane sampling if needed
1492  bool do_line = false; bool do_plane = false;
1493  pp.query("do_line_sampling",do_line); pp.query("do_plane_sampling",do_plane);
1494  if (do_line) {
1495  if (line_sampling_interval < 0 && line_sampling_per < 0) {
1496  Abort("Need to specify line_sampling_interval or line_sampling_per");
1497  }
1498  line_sampler = std::make_unique<LineSampler>();
1499  line_sampler->write_coords(z_phys_cc, geom);
1500  }
1501  if (do_plane) {
1503  Abort("Need to specify plane_sampling_interval or plane_sampling_per");
1504  }
1505  plane_sampler = std::make_unique<PlaneSampler>();
1506  }
1507 
1508  if ( solverChoice.terrain_type == TerrainType::EB ||
1509  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
1510  solverChoice.buildings_type == BuildingsType::ImmersedForcing )
1511  {
1512  bool write_eb_surface = false;
1513  pp.query("write_eb_surface", write_eb_surface);
1514  if (write_eb_surface) {
1515  if (verbose > 0) {
1516  amrex::Print() << "Writing the geometry to a vtp file.\n" << std::endl;
1517  }
1518  WriteEBSurface(grids[finest_level],dmap[finest_level],Geom(finest_level),&EBFactory(finest_level));
1519  }
1520  }
1521 }
constexpr amrex::Real three
Definition: ERF_Constants.H:11
const amrex::Real * prob_hi
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:17
void thinbody_wall_dist(std::unique_ptr< MultiFab > &wdist, Vector< IntVect > &xfaces, Vector< IntVect > &yfaces, Vector< IntVect > &zfaces, const Geometry &geomdata, std::unique_ptr< MultiFab > &z_phys_cc)
Definition: ERF_ThinBodyWallDist.cpp:12
static int last_check_file_step
Definition: ERF.H:1113
amrex::Vector< std::string > samplelinelogname
Definition: ERF.H:1717
void setRayleighRefFromSounding(bool restarting)
Set Rayleigh mean profiles from input sounding.
Definition: ERF_InitRayleigh.cpp:83
static amrex::Real last_plot2d_file_time_2
Definition: ERF.H:1118
amrex::Vector< amrex::IntVect > sampleline
Definition: ERF.H:1718
amrex::Vector< std::string > subvol3d_var_names
Definition: ERF.H:1201
void project_initial_velocity(int lev, amrex::Real time, amrex::Real dt)
Definition: ERF_PoissonSolve.cpp:31
amrex::Real m_plot2d_per_1
Definition: ERF.H:1186
amrex::Real plane_sampling_per
Definition: ERF.H:1701
static amrex::Real last_plot2d_file_time_1
Definition: ERF.H:1117
static amrex::Real sum_per
Definition: ERF.H:1314
static int last_plot2d_file_step_2
Definition: ERF.H:1112
void setRecordDataInfo(int i, const std::string &filename)
Definition: ERF.H:1624
static bool plot_file_on_restart
Definition: ERF.H:1124
amrex::Vector< std::string > lsm_flux_name
Definition: ERF.H:968
void sum_derived_quantities(double time)
Definition: ERF_WriteScalarProfiles.cpp:177
std::unique_ptr< amrex::MultiFab > wrf_PHB
Definition: ERF.H:1368
amrex::Vector< std::unique_ptr< std::fstream > > samplelinelog
Definition: ERF.H:1716
static amrex::Real last_plot3d_file_time_2
Definition: ERF.H:1116
int m_plot2d_int_2
Definition: ERF.H:1179
int m_plot3d_int_1
Definition: ERF.H:1176
static int sum_interval
Definition: ERF.H:1312
static int pert_interval
Definition: ERF.H:1313
amrex::Real line_sampling_per
Definition: ERF.H:1700
void restart()
Definition: ERF.cpp:1756
static int last_plot3d_file_step_2
Definition: ERF.H:1110
amrex::Real m_plot2d_per_2
Definition: ERF.H:1187
amrex::Real m_check_per
Definition: ERF.H:1199
int profile_int
Definition: ERF.H:1193
bool destag_profiles
Definition: ERF.H:1194
void compute_max_pressure_gradient_diagnostic(int lev)
Definition: ERF_Diagnostics.cpp:12
int m_check_int
Definition: ERF.H:1198
void write_1D_profiles(double time)
Definition: ERF_Write1DProfiles.cpp:17
void initRayleigh_at_level(const int &lev)
Initialize Rayleigh damping profiles at a level.
Definition: ERF_InitRayleigh.cpp:14
void appendPlotVariables(const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile.cpp:176
amrex::Vector< std::string > tot_e_datalogname
Definition: ERF.H:1710
static int output_bndry_planes
Definition: ERF.H:1381
static std::string nc_bdy_file
Definition: ERF.H:1332
void AverageDown()
Definition: ERF_AverageDown.cpp:16
void write_1D_profiles_stag(double time)
Definition: ERF_Write1DProfiles_stag.cpp:25
void Write2DPlotFile(int which, PlotFileType plotfile_type, amrex::Vector< std::string > plot_var_names)
Definition: ERF_Plotfile2D.cpp:130
static amrex::Real bndry_output_planes_start_time
Definition: ERF.H:1384
amrex::Vector< std::string > sampleptlogname
Definition: ERF.H:1713
amrex::Vector< amrex::Real > m_subvol_per
Definition: ERF.H:1182
void setRecordDerDataInfo(int i, const std::string &filename)
Definition: ERF.H:1637
amrex::Vector< std::unique_ptr< std::fstream > > sampleptlog
Definition: ERF.H:1712
void poisson_wall_dist(int lev)
Definition: ERF_PoissonWallDist.cpp:22
void sum_integrated_quantities(double time)
Definition: ERF_WriteScalarProfiles.cpp:15
std::unique_ptr< WriteBndryPlanes > m_w2d
Definition: ERF.H:1444
void init_geo_wind_profile(const std::string input_file, amrex::Vector< amrex::Real > &u_geos, amrex::Gpu::DeviceVector< amrex::Real > &u_geos_d, amrex::Vector< amrex::Real > &v_geos, amrex::Gpu::DeviceVector< amrex::Real > &v_geos_d, const amrex::Geometry &lgeom, const amrex::Vector< amrex::Real > &zlev_stag)
Definition: ERF_InitGeowind.cpp:10
void WriteSubvolume(int isub, amrex::Vector< std::string > subvol_var_names)
Definition: ERF_WriteSubvolume.cpp:145
void sum_energy_quantities(double time)
Definition: ERF_WriteScalarProfiles.cpp:313
amrex::Vector< std::string > lsm_data_name
Definition: ERF.H:966
void initSponge()
Initialize sponge profiles.
Definition: ERF_InitSponge.cpp:35
std::unique_ptr< PlaneSampler > plane_sampler
Definition: ERF.H:1703
amrex::Real m_plot3d_per_2
Definition: ERF.H:1185
amrex::Vector< std::unique_ptr< std::fstream > > tot_e_datalog
Definition: ERF.H:1707
int real_width
Definition: ERF.H:1333
amrex::Vector< int > last_subvol_step
Definition: ERF.H:1121
static PlotFileType plotfile3d_type_2
Definition: ERF.H:1318
void setRecordEnergyDataInfo(int i, const std::string &filename)
Definition: ERF.H:1650
void Interp2DArrays(int lev, const amrex::BoxArray &my_ba2d, const amrex::DistributionMapping &my_dm)
Definition: ERF.cpp:1524
static PlotFileType plotfile2d_type_2
Definition: ERF.H:1320
int plane_sampling_interval
Definition: ERF.H:1699
int m_plot2d_int_1
Definition: ERF.H:1178
void WriteCheckpointFile() const
Definition: ERF_Checkpoint.cpp:27
void Write3DPlotFile(int which, PlotFileType plotfile_type, amrex::Vector< std::string > plot_var_names)
Definition: ERF_Plotfile.cpp:265
static std::string nc_low_file
Definition: ERF.H:1337
static int last_plot2d_file_step_1
Definition: ERF.H:1111
amrex::Real m_plot3d_per_1
Definition: ERF.H:1184
void Construct_ERFFillPatchers(int lev)
Definition: ERF.cpp:2717
void setRecordSampleLineInfo(int i, int lev, amrex::IntVect &cell, const std::string &filename)
Definition: ERF.H:1680
void setSpongeRefFromSounding(bool restarting)
Set sponge mean profiles from input sounding.
Definition: ERF_InitSponge.cpp:65
int line_sampling_interval
Definition: ERF.H:1698
amrex::Vector< amrex::IntVect > samplepoint
Definition: ERF.H:1714
amrex::Vector< amrex::Real > last_subvol_time
Definition: ERF.H:1122
static amrex::Real last_check_file_time
Definition: ERF.H:1119
static int last_plot3d_file_step_1
Definition: ERF.H:1109
static amrex::Real last_plot3d_file_time_1
Definition: ERF.H:1115
std::unique_ptr< LineSampler > line_sampler
Definition: ERF.H:1702
static PlotFileType plotfile2d_type_1
Definition: ERF.H:1319
static PlotFileType plotfile3d_type_1
Definition: ERF.H:1317
amrex::Vector< int > m_subvol_int
Definition: ERF.H:1181
static bool is_it_time_for_action(int nstep, double time, amrex::Real dt, int action_interval, amrex::Real action_per)
Definition: ERF_WriteScalarProfiles.cpp:654
void setRecordSamplePointInfo(int i, int lev, amrex::IntVect &cell, const std::string &filename)
Definition: ERF.H:1663
int m_plot3d_int_2
Definition: ERF.H:1177
void ReadCheckpointFileSurfaceLayer()
Definition: ERF_Checkpoint.cpp:1222
@ nc
Definition: ERF_Morrison.H:44
bool have_zero_flux_faces
Definition: ERF_AdvStruct.H:440
amrex::Real rho0_trans
Definition: ERF_DiffStruct.H:91
amrex::Real dynamic_viscosity
Definition: ERF_DiffStruct.H:96
amrex::Real theta_ref_inp_sound
Definition: ERF_InputSoundingData.H:406
amrex::Real qv_ref_inp_sound
Definition: ERF_InputSoundingData.H:406
bool spatial_moisture_forcing
Definition: ERF_DataStruct.H:1322
bool any_perturbation() const
Definition: ERF_DataStruct.H:1369
bool have_geo_wind_profile
Definition: ERF_DataStruct.H:1399
std::string abl_geo_wind_table
Definition: ERF_DataStruct.H:1398
amrex::Vector< amrex::Vector< amrex::Real > > vert_implicit_fac
Definition: ERF_DataStruct.H:1246
bool use_rotate_surface_flux
Definition: ERF_DataStruct.H:1329
bool do_forest_drag
Definition: ERF_DataStruct.H:1424
bool spatial_rhotheta_forcing
Definition: ERF_DataStruct.H:1321
void debug(amrex::Real)
Definition: ERF_TurbPertStruct.H:640
Here is the call graph for this function:

◆ InitData_pre()

void ERF::InitData_pre ( )
553 {
554  // Initialize the start time for our CPU-time tracker
555  startCPUTime = Real(ParallelDescriptor::second());
556 
557  // Create the ReadBndryPlanes object so we can read boundary plane data
558  // m_r2d is used by init_bcs so we must instantiate this class before
559  if (input_bndry_planes) {
560  Print() << "Defining r2d for the first time " << std::endl;
561  m_r2d = std::make_unique<ReadBndryPlanes>(geom[0], solverChoice.rdOcp);
562  }
563 
564  if (restart_chkfile.empty()) {
565  // Start simulation from the beginning
566  InitFromScratch(zero);
567  } else {
568  // For initialization this is done in init_only; it is done here for restart
569  init_bcs();
570  }
571 
572  solverChoice.check_params(max_level,geom,phys_bc_type);
573 }
void init_bcs()
Definition: ERF_InitBCs.cpp:299
void check_params(int max_level, const amrex::Vector< amrex::Geometry > &geom_vect, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > phys_bc_type)
Definition: ERF_DataStruct.H:903

◆ initHSE() [1/2]

void ERF::initHSE ( )
private

Initialize HSE.

193 {
194  for (int lev = 0; lev <= finest_level; lev++)
195  {
196  initHSE(lev);
197  }
198 }

◆ initHSE() [2/2]

void ERF::initHSE ( int  lev)
private

Initialize density and pressure base state in hydrostatic equilibrium.

20 {
21  // This integrates up through column to update p_hse, pi_hse, th_hse;
22  // r_hse is not const b/c FillBoundary is called at the end for r_hse and p_hse
23 
24  MultiFab r_hse (base_state[lev], make_alias, BaseState::r0_comp, 1);
25  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
26  MultiFab pi_hse(base_state[lev], make_alias, BaseState::pi0_comp, 1);
27  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
28  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp, 1);
29 
30  bool all_boxes_touch_bottom = true;
31  Box domain(geom[lev].Domain());
32 
33  int icomp = 0; int ncomp = BaseState::num_comps;
34 
35  if (lev == 0) {
36  BoxArray ba(base_state[lev].boxArray());
37  for (int i = 0; i < ba.size(); i++) {
38  if (ba[i].smallEnd(2) != domain.smallEnd(2)) {
39  all_boxes_touch_bottom = false;
40  }
41  }
42  }
43  else
44  {
45  //
46  // We need to do this interp from coarse level in order to set the values of
47  // the base state inside the domain but outside of the fine region
48  //
49  base_state[lev-1].FillBoundary(geom[lev-1].periodicity());
50  //
51  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
52  // have been pre-filled - this includes ghost cells both inside and outside
53  // the domain
54  //
55  InterpFromCoarseLevel(base_state[lev], base_state[lev].nGrowVect(),
56  IntVect(0,0,0), // do not fill ghost cells outside the domain
57  base_state[lev-1], icomp, icomp, ncomp,
58  geom[lev-1], geom[lev],
59  refRatio(lev-1), &cell_cons_interp,
61 
62  // We need to do this here because the interpolation above may leave corners unfilled
63  // when the corners need to be filled by, for example, reflection of the fine ghost
64  // cell outside the fine region but inide the domain.
65  (*physbcs_base[lev])(base_state[lev],icomp,ncomp,base_state[lev].nGrowVect());
66  }
67 
68  bool is_constant_dz = (solverChoice.mesh_type == MeshType::ConstantDz);
69  bool is_stretched_dz = (solverChoice.mesh_type == MeshType::StretchedDz);
70 
71  if (all_boxes_touch_bottom || lev > 0) {
72 
73  // Initial r_hse may or may not be in HSE -- defined in ERF_Prob.cpp
74  if ( (solverChoice.init_type == InitType::MoistBaseState) ||
75  (solverChoice.init_type == InitType::HindCast) )
76  {
77  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type == MeshType::ConstantDz);
78  prob->erf_init_dens_hse_moist(r_hse, z_phys_nd[lev], geom[lev]);
79 
80  }
81  else if (solverChoice.init_type == InitType::ConstantDensity)
82  {
83  // In this case we set rho from user-specified values, then integrate
84  // to define p from HSE (even if gravity = 0), then compute theta from (p,rho)
85  prob->erf_init_const_dens_hse(r_hse);
86  }
87  else if (solverChoice.init_type == InitType::Uniform)
88  {
89  // In this case we set both rho and theta from user-specified values
91  prob->erf_init_const_dens_and_th_hse(r_hse,p_hse,pi_hse,th_hse,qv_hse,solverChoice.rdOcp);
92  }
93  else if (solverChoice.init_type == InitType::ConstantDensityLinearTheta)
94  {
95  // In this case we set both rho and theta from user-specified values
97  prob->erf_init_const_dens_and_linear_th_hse(r_hse,p_hse,pi_hse,th_hse,qv_hse,
99  }
100  else
101  {
102  // In this case we set rho from user-specified values, then integrate
103  // to define p from HSE (even if gravity = 0), then compute theta from (p,rho)
104  prob->erf_init_dens_hse_dry(r_hse, z_phys_nd[lev], z_phys_cc[lev], geom[lev], stretched_dz_h[lev],
105  is_constant_dz, is_stretched_dz);
106  }
107 
108  if (solverChoice.init_type != InitType::Uniform && solverChoice.init_type !=InitType::ConstantDensityLinearTheta) {
109  erf_enforce_hse(lev, r_hse, p_hse, pi_hse, th_hse, qv_hse, z_phys_cc[lev]);
110  }
111 
112  //
113  // Impose physical bc's on the base state
114  //
115  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
116 
117  } else {
118 
119  BoxArray ba_new(domain);
120 
121  ChopGrids2D(ba_new, domain, ParallelDescriptor::NProcs());
122 
123  DistributionMapping dm_new(ba_new);
124 
125  MultiFab new_base_state(ba_new, dm_new, BaseState::num_comps, base_state[lev].nGrowVect());
126  new_base_state.ParallelCopy(base_state[lev],0,0,base_state[lev].nComp(),
127  base_state[lev].nGrowVect(),base_state[lev].nGrowVect());
128 
129  MultiFab new_r_hse (new_base_state, make_alias, BaseState::r0_comp, 1);
130  MultiFab new_p_hse (new_base_state, make_alias, BaseState::p0_comp, 1);
131  MultiFab new_pi_hse(new_base_state, make_alias, BaseState::pi0_comp, 1);
132  MultiFab new_th_hse(new_base_state, make_alias, BaseState::th0_comp, 1);
133  MultiFab new_qv_hse(new_base_state, make_alias, BaseState::qv0_comp, 1);
134 
135  std::unique_ptr<MultiFab> new_z_phys_cc;
136  std::unique_ptr<MultiFab> new_z_phys_nd;
137  if (solverChoice.mesh_type != MeshType::ConstantDz) {
138  new_z_phys_cc = std::make_unique<MultiFab>(ba_new,dm_new,1,1);
139  new_z_phys_cc->ParallelCopy(*z_phys_cc[lev],0,0,1,1,1);
140 
141  BoxArray ba_new_nd(ba_new);
142  ba_new_nd.surroundingNodes();
143  new_z_phys_nd = std::make_unique<MultiFab>(ba_new_nd,dm_new,1,1);
144  new_z_phys_nd->ParallelCopy(*z_phys_nd[lev],0,0,1,1,1);
145  }
146 
147  // Initial r_hse may or may not be in HSE -- defined in ERF_Prob.cpp
148  if (solverChoice.init_type == InitType::MoistBaseState) {
149  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type == MeshType::ConstantDz);
150  prob->erf_init_dens_hse_moist(new_r_hse, new_z_phys_nd, geom[lev]);
151  } else if (solverChoice.init_type == InitType::ConstantDensity) {
152 
153  // In this case we set rho from user-specified values, then integrate
154  // to define p from HSE (even if gravity = 0), then compute theta from (p,rho)
155  prob->erf_init_const_dens_hse(new_r_hse);
156 
157  } else if (solverChoice.init_type == InitType::Uniform) {
158 
159  // In this case we set both rho and theta from user-specified values
161  prob->erf_init_const_dens_and_th_hse(new_r_hse,new_p_hse,new_pi_hse,new_th_hse,new_qv_hse,solverChoice.rdOcp);
162 
163  } else {
164  prob->erf_init_dens_hse_dry(new_r_hse, new_z_phys_nd, new_z_phys_cc, geom[lev], stretched_dz_h[lev],
165  is_constant_dz, is_stretched_dz);
166  }
167 
168  erf_enforce_hse(lev, new_r_hse, new_p_hse, new_pi_hse, new_th_hse, new_qv_hse, new_z_phys_cc);
169 
170  //
171  // Impose physical bc's on the base state (we must make new, temporary bcs object because the z_phys_nd is different)
172  //
173  ERFPhysBCFunct_base* temp_physbcs_base =
174  new ERFPhysBCFunct_base(lev, geom[lev], domain_bcs_type, domain_bcs_type_d, new_z_phys_nd,
175  (solverChoice.terrain_type == TerrainType::MovingFittedMesh));
176  (*temp_physbcs_base)(new_base_state,0,new_base_state.nComp(),new_base_state.nGrowVect());
177  delete temp_physbcs_base;
178 
179  // Now copy back into the original arrays
180  base_state[lev].ParallelCopy(new_base_state,0,0,base_state[lev].nComp(),
181  base_state[lev].nGrowVect(),base_state[lev].nGrowVect());
182  }
183 
184  //
185  // Impose physical bc's on the base state -- the values outside the fine region
186  // but inside the domain have already been filled in the call above to InterpFromCoarseLevel
187  //
188  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
189 }
void ChopGrids2D(BoxArray &ba, const Box &domain, int target_size)
Definition: ERF_ChopGrids.cpp:21
Definition: ERF_PhysBCFunct.H:286
void erf_enforce_hse(int lev, amrex::MultiFab &dens, amrex::MultiFab &pres, amrex::MultiFab &pi, amrex::MultiFab &th, amrex::MultiFab &qv, std::unique_ptr< amrex::MultiFab > &z_cc)
Definition: ERF_Init1D.cpp:210
Here is the call graph for this function:

◆ initialize_integrator()

void ERF::initialize_integrator ( int  lev,
amrex::MultiFab &  cons_mf,
amrex::MultiFab &  vel_mf 
)
private
879 {
880  const BoxArray& ba(cons_mf.boxArray());
881  const DistributionMapping& dm(cons_mf.DistributionMap());
882 
883  int ncomp_cons = cons_mf.nComp();
884 
885  // Initialize the integrator memory
886  Vector<MultiFab> int_state; // integration state data structure example
887  int_state.push_back(MultiFab(cons_mf, make_alias, 0, ncomp_cons)); // cons
888  int_state.push_back(MultiFab(convert(ba,IntVect(1,0,0)), dm, 1, vel_mf.nGrow())); // xmom
889  int_state.push_back(MultiFab(convert(ba,IntVect(0,1,0)), dm, 1, vel_mf.nGrow())); // ymom
890  int_state.push_back(MultiFab(convert(ba,IntVect(0,0,1)), dm, 1, vel_mf.nGrow())); // zmom
891 
892  mri_integrator_mem[lev] = std::make_unique<MRISplitIntegrator<Vector<MultiFab> > >(int_state);
893  mri_integrator_mem[lev]->setNoSubstepping((solverChoice.substepping_type[lev] == SubsteppingType::None));
894  mri_integrator_mem[lev]->setAnelastic(solverChoice.anelastic[lev]);
895  mri_integrator_mem[lev]->setNcompCons(ncomp_cons);
896  mri_integrator_mem[lev]->setForceFirstStageSingleSubstep(solverChoice.force_stage1_single_substep);
897 }

◆ InitializeFromFile()

void ERF::InitializeFromFile ( )
private

◆ InitializeLevelFromData()

void ERF::InitializeLevelFromData ( int  lev,
const amrex::MultiFab &  initial_data 
)
private

◆ initializeMicrophysics()

void ERF::initializeMicrophysics ( const int &  a_nlevsmax)
private
Parameters
a_nlevsmaxnumber of AMR levels
1723 {
1724  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Eulerian) {
1725 
1726  micro = std::make_unique<EulerianMicrophysics>(a_nlevsmax, solverChoice.moisture_type);
1727 
1728  } else if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
1729 #ifdef ERF_USE_PARTICLES
1730  micro = std::make_unique<LagrangianMicrophysics>(a_nlevsmax, solverChoice.moisture_type);
1731  /* Lagrangian microphysics models will have a particle container; it needs to be added
1732  to ERF::particleData */
1733  const auto& pc_name( dynamic_cast<LagrangianMicrophysics&>(*micro).getName() );
1734  /* The particle container has not yet been constructed and initialized, so just add
1735  its name here for now (so that functions to set plotting variables can see it). */
1736  particleData.addName( pc_name );
1737 
1738 #else
1739  Abort("Lagrangian microphysics can be used when compiled with ERF_USE_PARTICLES");
1740 #endif
1741  }
1742 
1743  qmoist.resize(a_nlevsmax);
1744  return;
1745 }
amrex::Vector< amrex::Vector< amrex::MultiFab * > > qmoist
Definition: ERF.H:950

Referenced by ERF_shared().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ initRayleigh_at_level()

void ERF::initRayleigh_at_level ( const int &  lev)
private

Initialize Rayleigh damping profiles at a level.

Initialization function for host and device vectors used to store averaged quantities when calculating the effects of Rayleigh Damping.

15 {
16  const int khi = geom[0].Domain().bigEnd(2);
17  solverChoice.dampingChoice.rayleigh_ztop = (solverChoice.terrain_type == TerrainType::None) ? geom[0].ProbHi(2) : zlevels_stag[0][khi+1];
18 
19  // These have 4 components: ubar, vbar, wbar, thetabar
20  h_rayleigh_ptrs[lev].resize(Rayleigh::nvars);
21  d_rayleigh_ptrs[lev].resize(Rayleigh::nvars);
22 
23  const int zlen_rayleigh = geom[lev].Domain().length(2);
24 
25  // Allocate space for these 1D vectors
26  for (int n = 0; n < Rayleigh::nvars; n++) {
27  h_rayleigh_ptrs[lev][n].resize(zlen_rayleigh, 0.0_rt);
28  d_rayleigh_ptrs[lev][n].resize(zlen_rayleigh, 0.0_rt);
29  }
30 
31  h_sinesq_ptrs[lev].resize(zlen_rayleigh);
32  d_sinesq_ptrs[lev].resize(zlen_rayleigh);
33 
34  h_sinesq_stag_ptrs[lev].resize(zlen_rayleigh+1);
35  d_sinesq_stag_ptrs[lev].resize(zlen_rayleigh+1);
36 
39 
40  for (int k = 0; k < zlen_rayleigh; k++) {
41  Real z = myhalf * (zlevels_stag[lev][k] + zlevels_stag[lev][k+1]);
42  if (z > (ztop - zdamp)) {
43  Real zfrac = one - (ztop - z) / zdamp;
44  Real s = std::sin(PIoTwo*zfrac);
45  h_sinesq_ptrs[lev][k] = s*s;
46  } else {
47  h_sinesq_ptrs[lev][k] = zero;
48  }
49  }
50 
51  for (int k = 0; k < zlen_rayleigh+1; k++) {
52  Real z = zlevels_stag[lev][k];
53  if (z > (ztop - zdamp)) {
54  Real zfrac = one - (ztop - z) / zdamp;
55  Real s = std::sin(PIoTwo*zfrac);
56  h_sinesq_stag_ptrs[lev][k] = s*s;
57  } else {
58  h_sinesq_stag_ptrs[lev][k] = zero;
59  }
60  }
61 
62  // Init the host vectors for the reference states
63  prob->erf_init_rayleigh(h_rayleigh_ptrs[lev], geom[lev], z_phys_nd[lev],
65 
66  // Copy from host vectors to device vectors
67  for (int n = 0; n < Rayleigh::nvars; n++) {
68  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][n].begin(), h_rayleigh_ptrs[lev][n].end(),
69  d_rayleigh_ptrs[lev][n].begin());
70  }
71  Gpu::copy(Gpu::hostToDevice, h_sinesq_ptrs[lev].begin(), h_sinesq_ptrs[lev].end(), d_sinesq_ptrs[lev].begin());
72  Gpu::copy(Gpu::hostToDevice, h_sinesq_stag_ptrs[lev].begin(), h_sinesq_stag_ptrs[lev].end(), d_sinesq_stag_ptrs[lev].begin());
73 }
constexpr amrex::Real PIoTwo
Definition: ERF_Constants.H:38
amrex::Real rayleigh_ztop
Definition: ERF_DampingStruct.H:90
amrex::Real rayleigh_zdamp
Definition: ERF_DampingStruct.H:89

◆ initSponge()

void ERF::initSponge ( )
private

Initialize sponge profiles.

Initialization function for host and device vectors used to store the effects of sponge Damping.

36 {
37  h_sponge_ptrs.resize(max_level+1);
38  d_sponge_ptrs.resize(max_level+1);
39 
40  for (int lev = 0; lev <= finest_level; lev++)
41  {
42  // These have 2 components: ubar, vbar
45 
46  const int zlen_sponge = geom[lev].Domain().length(2);
47 
48  // Allocate space for these 1D vectors
49  for (int n = 0; n < Sponge::nvars_sponge; n++) {
50  h_sponge_ptrs[lev][n].resize(zlen_sponge, 0.0_rt);
51  d_sponge_ptrs[lev][n].resize(zlen_sponge, 0.0_rt);
52  }
53 
54  }
55 }
amrex::Vector< amrex::Vector< amrex::Vector< amrex::Real > > > h_sponge_ptrs
Definition: ERF.H:1416

◆ input_sponge()

void ERF::input_sponge ( int  lev)

High level wrapper for sponge x and y velocities level data from input sponge data.

Parameters
levInteger specifying the current level
18 {
19  // We only want to read the file once
20  if (lev == 0) {
22  Error("input_sounding file name must be provided via input");
23 
24  // this will interpolate the input profiles to the nominal height levels
25  // (ranging from 0 to the domain top)
27  }
28 }
InputSpongeData input_sponge_data
Definition: ERF.H:857
void read_from_file(const amrex::Geometry &geom, const amrex::Vector< amrex::Real > &zlevels_stag)
Definition: ERF_InputSpongeData.H:28
std::string input_sponge_file
Definition: ERF_InputSpongeData.H:108

◆ Interp2DArrays()

void ERF::Interp2DArrays ( int  lev,
const amrex::BoxArray &  my_ba2d,
const amrex::DistributionMapping &  my_dm 
)
1525 {
1526  if (lev == 0) { return; }
1527 
1528  if (lon_m[lev-1] && !lon_m[lev]) {
1529  auto ngv = lon_m[lev-1]->nGrowVect(); ngv[2] = 0;
1530  lon_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
1531  InterpFromCoarseLevel(*lon_m[lev], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
1532  *lon_m[lev-1], 0, 0, 1,
1533  geom[lev-1], geom[lev],
1534  refRatio(lev-1), &cell_cons_interp,
1536  }
1537  if (lat_m[lev-1] && !lat_m[lev]) {
1538  auto ngv = lat_m[lev-1]->nGrowVect(); ngv[2] = 0;
1539  lat_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
1540  InterpFromCoarseLevel(*lat_m[lev], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
1541  *lat_m[lev-1], 0, 0, 1,
1542  geom[lev-1], geom[lev],
1543  refRatio(lev-1), &cell_cons_interp,
1545  }
1546  if (sinPhi_m[lev-1] && !sinPhi_m[lev]) {
1547  auto ngv = sinPhi_m[lev-1]->nGrowVect(); ngv[2] = 0;
1548  sinPhi_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
1549  InterpFromCoarseLevel(*sinPhi_m[lev], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
1550  *sinPhi_m[lev-1], 0, 0, 1,
1551  geom[lev-1], geom[lev],
1552  refRatio(lev-1), &cell_cons_interp,
1554  }
1555  if (cosPhi_m[lev-1] && !cosPhi_m[lev]) {
1556  auto ngv = cosPhi_m[lev-1]->nGrowVect(); ngv[2] = 0;
1557  cosPhi_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
1558  InterpFromCoarseLevel(*cosPhi_m[lev], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
1559  *cosPhi_m[lev-1], 0, 0, 1,
1560  geom[lev-1], geom[lev],
1561  refRatio(lev-1), &cell_cons_interp,
1563  }
1564  if (sst_lev[lev-1][0]) {
1565  if (sst_lev[lev].size() < sst_lev[lev-1].size()) {
1566  sst_lev[lev].resize(sst_lev[lev-1].size());
1567  }
1568 #ifdef ERF_USE_NETCDF
1569  double time_since_start_low = t_new[0] + start_time - start_low_time;
1570  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
1571  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(sst_lev[lev-1].size()));
1572 #else
1573  // TODO: Fix if SST is provided without NETCDF
1574  int n_time_old = 0;
1575  int ntimes_to_interp = 1;
1576 #endif
1577  auto ngv = sst_lev[lev-1][0]->nGrowVect(); ngv[2] = 0;
1578 
1579  for (int n = n_time_old; n < ntimes_to_interp; n++) {
1580  if (!sst_lev[lev-1][n]) { continue; }
1581  if (!sst_lev[lev][n]) {
1582  sst_lev[lev][n] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
1583  InterpFromCoarseLevel(*sst_lev[lev][n], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
1584  *sst_lev[lev-1][n], 0, 0, 1,
1585  geom[lev-1], geom[lev],
1586  refRatio(lev-1), &cell_cons_interp,
1588  }
1589  }
1590  }
1591  if (tsk_lev[lev-1][0]) {
1592  if (tsk_lev[lev].size() < tsk_lev[lev-1].size()) {
1593  tsk_lev[lev].resize(tsk_lev[lev-1].size());
1594  }
1595 #ifdef ERF_USE_NETCDF
1596  double time_since_start_low = t_new[0] + start_time - start_low_time;
1597  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
1598  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(tsk_lev[lev-1].size()));
1599 #else
1600  // TODO: Fix if TSK is provided without NETCDF
1601  int n_time_old = 0;
1602  int ntimes_to_interp = 1;
1603 #endif
1604  auto ngv = tsk_lev[lev-1][0]->nGrowVect(); ngv[2] = 0;
1605 
1606  for (int n = n_time_old; n < ntimes_to_interp; n++) {
1607  if (!tsk_lev[lev-1][n]) { continue; }
1608  if (!tsk_lev[lev][n]) {
1609  tsk_lev[lev][n] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
1610  InterpFromCoarseLevel(*tsk_lev[lev][n], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
1611  *tsk_lev[lev-1][n], 0, 0, 1,
1612  geom[lev-1], geom[lev],
1613  refRatio(lev-1), &cell_cons_interp,
1615  }
1616  }
1617  }
1618 
1619  Real time_for_fp = zero; // This is not actually used
1620  Vector<Real> ftime = {time_for_fp, time_for_fp};
1621  Vector<Real> ctime = {time_for_fp, time_for_fp};
1622  if (lat_m[lev]) {
1623  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
1624  Vector<MultiFab*> fmf = {lat_m[lev ].get(), lat_m[lev ].get()};
1625  Vector<MultiFab*> cmf = {lat_m[lev-1].get(), lat_m[lev-1].get()};
1626  IntVect ngv = lat_m[lev]->nGrowVect(); ngv[2] = 0;
1627  Interpolater* mapper = &cell_cons_interp;
1628  FillPatchTwoLevels(*lat_m[lev].get(), ngv, IntVect(0,0,0),
1629  time_for_fp, cmf, ctime, fmf, ftime,
1630  0, 0, 1, geom[lev-1], geom[lev],
1631  refRatio(lev-1), mapper, domain_bcs_type,
1632  BCVars::cons_bc);
1633  }
1634  if (lon_m[lev]) {
1635  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
1636  Vector<MultiFab*> fmf = {lon_m[lev ].get(), lon_m[lev ].get()};
1637  Vector<MultiFab*> cmf = {lon_m[lev-1].get(), lon_m[lev-1].get()};
1638  IntVect ngv = lon_m[lev]->nGrowVect(); ngv[2] = 0;
1639  Interpolater* mapper = &cell_cons_interp;
1640  FillPatchTwoLevels(*lon_m[lev].get(), ngv, IntVect(0,0,0),
1641  time_for_fp, cmf, ctime, fmf, ftime,
1642  0, 0, 1, geom[lev-1], geom[lev],
1643  refRatio(lev-1), mapper, domain_bcs_type,
1644  BCVars::cons_bc);
1645  } // lon_m
1646  if (sinPhi_m[lev]) {
1647  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
1648  Vector<MultiFab*> fmf = {sinPhi_m[lev ].get(), sinPhi_m[lev ].get()};
1649  Vector<MultiFab*> cmf = {sinPhi_m[lev-1].get(), sinPhi_m[lev-1].get()};
1650  IntVect ngv = sinPhi_m[lev]->nGrowVect(); ngv[2] = 0;
1651  Interpolater* mapper = &cell_cons_interp;
1652  FillPatchTwoLevels(*sinPhi_m[lev].get(), ngv, IntVect(0,0,0),
1653  time_for_fp, cmf, ctime, fmf, ftime,
1654  0, 0, 1, geom[lev-1], geom[lev],
1655  refRatio(lev-1), mapper, domain_bcs_type,
1656  BCVars::cons_bc);
1657  } // sinPhi
1658  if (cosPhi_m[lev]) {
1659  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
1660  Vector<MultiFab*> fmf = {cosPhi_m[lev ].get(), cosPhi_m[lev ].get()};
1661  Vector<MultiFab*> cmf = {cosPhi_m[lev-1].get(), cosPhi_m[lev-1].get()};
1662  IntVect ngv = cosPhi_m[lev]->nGrowVect(); ngv[2] = 0;
1663  Interpolater* mapper = &cell_cons_interp;
1664  FillPatchTwoLevels(*cosPhi_m[lev].get(), ngv, IntVect(0,0,0),
1665  time_for_fp, cmf, ctime, fmf, ftime,
1666  0, 0, 1, geom[lev-1], geom[lev],
1667  refRatio(lev-1), mapper, domain_bcs_type,
1668  BCVars::cons_bc);
1669  } // cosPhi
1670  if (sst_lev[lev][0]) {
1671  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
1672 #ifdef ERF_USE_NETCDF
1673  double time_since_start_low = t_new[0] + start_time - start_low_time;
1674  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
1675  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(sst_lev[lev-1].size()));
1676 #else
1677  // TODO: Fix if SST is provided without NETCDF
1678  int n_time_old = 0;
1679  int ntimes_to_interp = 1;
1680 #endif
1681  for (int n = n_time_old; n < ntimes_to_interp; n++) {
1682  if (!sst_lev[lev][n] || !sst_lev[lev-1][n]) { continue; }
1683  Vector<MultiFab*> fmf = {sst_lev[lev ][n].get(), sst_lev[lev ][n].get()};
1684  Vector<MultiFab*> cmf = {sst_lev[lev-1][n].get(), sst_lev[lev-1][n].get()};
1685  IntVect ngv = sst_lev[lev][n]->nGrowVect(); ngv[2] = 0;
1686  Interpolater* mapper = &cell_cons_interp;
1687  FillPatchTwoLevels(*sst_lev[lev][n].get(), ngv, IntVect(0,0,0),
1688  time_for_fp, cmf, ctime, fmf, ftime,
1689  0, 0, 1, geom[lev-1], geom[lev],
1690  refRatio(lev-1), mapper, domain_bcs_type,
1691  BCVars::cons_bc);
1692  } // ntimes
1693  } // sst_lev
1694  if (tsk_lev[lev][0]) {
1695  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
1696 #ifdef ERF_USE_NETCDF
1697  double time_since_start_low = t_new[0] + start_time - start_low_time;
1698  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
1699  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(tsk_lev[lev-1].size()));
1700 #else
1701  // TODO: Fix if TSK is provided without NETCDF
1702  int n_time_old = 0;
1703  int ntimes_to_interp = 1;
1704 #endif
1705  for (int n = n_time_old; n < ntimes_to_interp; n++) {
1706  if (!tsk_lev[lev][n] || !tsk_lev[lev-1][n]) { continue; }
1707  Vector<MultiFab*> fmf = {tsk_lev[lev ][n].get(), tsk_lev[lev ][n].get()};
1708  Vector<MultiFab*> cmf = {tsk_lev[lev-1][n].get(), tsk_lev[lev-1][n].get()};
1709  IntVect ngv = tsk_lev[lev][n]->nGrowVect(); ngv[2] = 0;
1710  Interpolater* mapper = &cell_cons_interp;
1711  FillPatchTwoLevels(*tsk_lev[lev][n].get(), ngv, IntVect(0,0,0),
1712  time_for_fp, cmf, ctime, fmf, ftime,
1713  0, 0, 1, geom[lev-1], geom[lev],
1714  refRatio(lev-1), mapper, domain_bcs_type,
1715  BCVars::cons_bc);
1716  } // ntimes
1717  } // tsk_lev
1718 }
Here is the call graph for this function:

◆ is_it_time_for_action()

bool ERF::is_it_time_for_action ( int  nstep,
double  time,
amrex::Real  dt,
int  action_interval,
amrex::Real  action_per 
)
static

Helper function which uses the current step number, time, and timestep to determine whether it is time to take an action specified at every interval of timesteps.

Parameters
nstepTimestep number
timeCurrent time
dtlevTimestep for the current level
action_intervalInterval in number of timesteps for taking action
action_perInterval in simulation time for taking action
655 {
656  bool int_test = (action_interval > 0 && nstep % action_interval == 0);
657 
658  bool per_test = false;
659  if (action_per > zero) {
660  const int num_per_old = static_cast<int>(amrex::Math::floor((time - dtlev) / action_per));
661  const int num_per_new = static_cast<int>(amrex::Math::floor((time) / action_per));
662 
663  if (num_per_old != num_per_new) {
664  per_test = true;
665  }
666  }
667 
668  return int_test || per_test;
669 }

◆ make_eb_box()

void ERF::make_eb_box ( )

◆ make_eb_regular()

void ERF::make_eb_regular ( )

◆ make_physbcs()

void ERF::make_physbcs ( int  lev)
private
901 {
902  if (SolverChoice::mesh_type == MeshType::VariableDz) {
903  AMREX_ALWAYS_ASSERT(z_phys_nd[lev] != nullptr);
904  }
905 
906  physbcs_cons[lev] = std::make_unique<ERFPhysBCFunct_cons> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
908  z_phys_nd[lev], solverChoice.use_real_bcs, th_bc_data[lev].data());
909  physbcs_u[lev] = std::make_unique<ERFPhysBCFunct_u> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
911  z_phys_nd[lev], solverChoice.use_real_bcs, xvel_bc_data[lev].data());
912  physbcs_v[lev] = std::make_unique<ERFPhysBCFunct_v> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
914  z_phys_nd[lev], solverChoice.use_real_bcs, yvel_bc_data[lev].data());
915  physbcs_w[lev] = std::make_unique<ERFPhysBCFunct_w> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
918  solverChoice.use_real_bcs, zvel_bc_data[lev].data());
919  physbcs_base[lev] = std::make_unique<ERFPhysBCFunct_base> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d, z_phys_nd[lev],
920  (solverChoice.terrain_type == TerrainType::MovingFittedMesh));
921 }
Here is the call graph for this function:

◆ make_subdomains()

void ERF::make_subdomains ( const amrex::BoxList &  ba,
amrex::Vector< amrex::BoxArray > &  bins 
)
7 {
8  Vector<BoxList> bins_bl;
9 
10  // Clear out any old bins
11  bins.clear();
12 
13  // Iterate over boxes
14  for (auto bx : bl)
15  {
16  bool added = false;
17 
18  // Try to add box to existing bin
19  for (int j = 0; j < bins_bl.size(); ++j) {
20  BoxList& bin = bins_bl[j];
21  bool touches = false;
22 
23  for (auto& b : bin)
24  {
25  Box gbx(bx); gbx.grow(1);
26  if (gbx.intersects(b)) {
27  touches = true;
28  break;
29  }
30  }
31 
32  if (touches) {
33  bin.push_back(bx);
34  added = true;
35  break;
36  }
37  }
38 
39  // If box couldn't be added to existing bin, create new bin
40  if (!added) {
41  BoxList new_bin;
42  new_bin.push_back(bx);
43  bins_bl.push_back(new_bin);
44  }
45  }
46 
47  // Convert the BoxLists to BoxArrays
48  for (int i = 0; i < bins_bl.size(); ++i) {
49  bins.push_back(BoxArray(bins_bl[i]));
50  }
51 }

◆ MakeDiagnosticAverage()

void ERF::MakeDiagnosticAverage ( amrex::Vector< amrex::Real > &  h_havg,
amrex::MultiFab &  S,
int  n 
)
2672 {
2673  // Get the number of cells in z at level 0
2674  int dir_z = AMREX_SPACEDIM-1;
2675  auto domain = geom[0].Domain();
2676  int size_z = domain.length(dir_z);
2677  int start_z = domain.smallEnd()[dir_z];
2678  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
2679 
2680  // resize the level 0 horizontal average vectors
2681  h_havg.resize(size_z, 0.0_rt);
2682 
2683  // Get the cell centered data and construct sums
2684 #ifdef _OPENMP
2685 #pragma omp parallel if (Gpu::notInLaunchRegion())
2686 #endif
2687  for (MFIter mfi(S); mfi.isValid(); ++mfi) {
2688  const Box& box = mfi.validbox();
2689  const IntVect& se = box.smallEnd();
2690  const IntVect& be = box.bigEnd();
2691 
2692  auto fab_arr = S[mfi].array();
2693 
2694  FArrayBox fab_reduce(box, 1, The_Async_Arena());
2695  auto arr_reduce = fab_reduce.array();
2696 
2697  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
2698  arr_reduce(i, j, k, 0) = fab_arr(i,j,k,n);
2699  });
2700 
2701  for (int k=se[dir_z]; k <= be[dir_z]; ++k) {
2702  Box kbox(box); kbox.setSmall(dir_z,k); kbox.setBig(dir_z,k);
2703  h_havg[k-start_z] += fab_reduce.sum<RunOn::Device>(kbox,0);
2704  }
2705  }
2706 
2707  // combine sums from different MPI ranks
2708  ParallelDescriptor::ReduceRealSum(h_havg.dataPtr(), static_cast<int>(h_havg.size()));
2709 
2710  // divide by the total number of cells we are averaging over
2711  for (int k = 0; k < size_z; ++k) {
2712  h_havg[k] /= area_z;
2713  }
2714 }
Here is the call graph for this function:

◆ MakeEBGeometry()

void ERF::MakeEBGeometry ( )

◆ MakeFilename_EyeTracker_latlon()

std::string ERF::MakeFilename_EyeTracker_latlon ( int  nstep)
52  {
53  // Ensure output directory exists
54  const std::string dir = "Output_StormTracker/latlon";
55  if (!fs::exists(dir)) {
56  fs::create_directories(dir);
57  }
58 
59  // Construct filename with zero-padded step
60  std::ostringstream oss;
61  oss << dir << "/storm_track_latlon" << std::setw(7) << std::setfill('0') << nstep << ".txt";
62  return oss.str();
63 }

◆ MakeFilename_EyeTracker_maxvel()

std::string ERF::MakeFilename_EyeTracker_maxvel ( int  nstep)
66  {
67  // Ensure output directory exists
68  const std::string dir = "Output_StormTracker/maxvel";
69  if (!fs::exists(dir)) {
70  fs::create_directories(dir);
71  }
72 
73  // Construct filename with zero-padded step
74  std::ostringstream oss;
75  oss << dir << "/storm_maxvel_" << std::setw(7) << std::setfill('0') << nstep << ".txt";
76  return oss.str();
77 }

◆ MakeFilename_EyeTracker_minpressure()

std::string ERF::MakeFilename_EyeTracker_minpressure ( int  nstep)
80  {
81  // Ensure output directory exists
82  const std::string dir = "Output_StormTracker/minpressure";
83  if (!fs::exists(dir)) {
84  fs::create_directories(dir);
85  }
86 
87  // Construct filename with zero-padded step
88  std::ostringstream oss;
89  oss << dir << "/storm_minpressure_" << std::setw(7) << std::setfill('0') << nstep << ".txt";
90  return oss.str();
91 }

◆ MakeHorizontalAverages()

void ERF::MakeHorizontalAverages ( )
2566 {
2567  int lev = 0;
2568 
2569  // First, average down all levels (if doing two-way coupling)
2570  if (solverChoice.coupling_type == CouplingType::TwoWay) {
2571  AverageDown();
2572  }
2573 
2574  MultiFab mf(grids[lev], dmap[lev], 5, 0);
2575 
2576  int zdir = 2;
2577  auto domain = geom[0].Domain();
2578 
2579  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
2580  bool is_anelastic = (solverChoice.anelastic[lev] == 1);
2581 
2582  for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
2583  const Box& bx = mfi.validbox();
2584  auto fab_arr = mf.array(mfi);
2585  auto const hse_arr = base_state[lev].const_array(mfi);
2586  auto const cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
2587  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
2588  Real dens = cons_arr(i, j, k, Rho_comp);
2589  fab_arr(i, j, k, 0) = dens;
2590  fab_arr(i, j, k, 1) = cons_arr(i, j, k, RhoTheta_comp) / dens;
2591  if (!use_moisture) {
2592  if (is_anelastic) {
2593  fab_arr(i,j,k,2) = hse_arr(i,j,k,BaseState::p0_comp);
2594  } else {
2595  fab_arr(i,j,k,2) = getPgivenRTh(cons_arr(i,j,k,RhoTheta_comp));
2596  }
2597  }
2598  });
2599  }
2600 
2601  if (use_moisture)
2602  {
2603  for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
2604  const Box& bx = mfi.validbox();
2605  auto fab_arr = mf.array(mfi);
2606  auto const hse_arr = base_state[lev].const_array(mfi);
2607  auto const cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
2608  int ncomp = vars_new[lev][Vars::cons].nComp();
2609 
2610  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
2611  Real dens = cons_arr(i, j, k, Rho_comp);
2612  if (is_anelastic) {
2613  fab_arr(i,j,k,2) = hse_arr(i,j,k,BaseState::p0_comp);
2614  } else {
2615  Real qv = cons_arr(i, j, k, RhoQ1_comp) / dens;
2616  fab_arr(i, j, k, 2) = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp), qv);
2617  }
2618  fab_arr(i, j, k, 3) = (ncomp > RhoQ1_comp ? cons_arr(i, j, k, RhoQ1_comp) / dens : zero);
2619  fab_arr(i, j, k, 4) = (ncomp > RhoQ2_comp ? cons_arr(i, j, k, RhoQ2_comp) / dens : zero);
2620  });
2621  }
2622 
2623  Gpu::HostVector<Real> h_avg_qv = sumToLine(mf,3,1,domain,zdir);
2624  Gpu::HostVector<Real> h_avg_qc = sumToLine(mf,4,1,domain,zdir);
2625  }
2626 
2627  // Sum in the horizontal plane
2628  Gpu::HostVector<Real> h_avg_density = sumToLine(mf,0,1,domain,zdir);
2629  Gpu::HostVector<Real> h_avg_temperature = sumToLine(mf,1,1,domain,zdir);
2630  Gpu::HostVector<Real> h_avg_pressure = sumToLine(mf,2,1,domain,zdir);
2631 
2632  // Divide by the total number of cells we are averaging over
2633  int size_z = domain.length(zdir);
2634  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
2635  int klen = static_cast<int>(h_avg_density.size());
2636 
2637  for (int k = 0; k < klen; ++k) {
2638  h_havg_density[k] /= area_z;
2639  h_havg_temperature[k] /= area_z;
2640  h_havg_pressure[k] /= area_z;
2641  if (solverChoice.moisture_type != MoistureType::None)
2642  {
2643  h_havg_qc[k] /= area_z;
2644  h_havg_qv[k] /= area_z;
2645  }
2646  } // k
2647 
2648  // resize device vectors
2649  d_havg_density.resize(size_z, 0.0_rt);
2650  d_havg_temperature.resize(size_z, 0.0_rt);
2651  d_havg_pressure.resize(size_z, 0.0_rt);
2652 
2653  // copy host vectors to device vectors
2654  Gpu::copy(Gpu::hostToDevice, h_havg_density.begin(), h_havg_density.end(), d_havg_density.begin());
2655  Gpu::copy(Gpu::hostToDevice, h_havg_temperature.begin(), h_havg_temperature.end(), d_havg_temperature.begin());
2656  Gpu::copy(Gpu::hostToDevice, h_havg_pressure.begin(), h_havg_pressure.end(), d_havg_pressure.begin());
2657 
2658  if (solverChoice.moisture_type != MoistureType::None)
2659  {
2660  d_havg_qv.resize(size_z, 0.0_rt);
2661  d_havg_qc.resize(size_z, 0.0_rt);
2662  Gpu::copy(Gpu::hostToDevice, h_havg_qv.begin(), h_havg_qv.end(), d_havg_qv.begin());
2663  Gpu::copy(Gpu::hostToDevice, h_havg_qc.begin(), h_havg_qc.end(), d_havg_qc.begin());
2664  }
2665 }
amrex::Gpu::DeviceVector< amrex::Real > d_havg_temperature
Definition: ERF.H:1437
amrex::Gpu::DeviceVector< amrex::Real > d_havg_qv
Definition: ERF.H:1439
amrex::Vector< amrex::Real > h_havg_pressure
Definition: ERF.H:1432
amrex::Vector< amrex::Real > h_havg_qc
Definition: ERF.H:1434
amrex::Vector< amrex::Real > h_havg_density
Definition: ERF.H:1430
amrex::Gpu::DeviceVector< amrex::Real > d_havg_qc
Definition: ERF.H:1440
amrex::Gpu::DeviceVector< amrex::Real > d_havg_density
Definition: ERF.H:1436
amrex::Vector< amrex::Real > h_havg_temperature
Definition: ERF.H:1431
amrex::Gpu::DeviceVector< amrex::Real > d_havg_pressure
Definition: ERF.H:1438
amrex::Vector< amrex::Real > h_havg_qv
Definition: ERF.H:1433
Here is the call graph for this function:

◆ MakeNewLevelFromCoarse()

void ERF::MakeNewLevelFromCoarse ( int  lev,
amrex::Real  time,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
override
277 {
278  //
279  // Note that "time" here is elapsed time
280  //
281  AMREX_ALWAYS_ASSERT(lev > 0);
282 
283  if (verbose) {
284  amrex::Print() <<" NEW BA FROM COARSE AT LEVEL " << lev << " " << ba << std::endl;
285  }
286 
287  //
288  // Grow the subdomains vector and build the subdomains vector at this level
289  //
290  subdomains.resize(lev+1);
291  //
292  // Create subdomains at each level within the domain such that
293  // 1) all boxes in a given subdomain are "connected"
294  // 2) no boxes in a subdomain touch any boxes in any other subdomain
295  //
296  make_subdomains(ba.simplified_list(), subdomains[lev]);
297 
298  if (lev == 0) init_bcs();
299 
300  //********************************************************************************************
301  // This allocates all kinds of things, including but not limited to: solution arrays,
302  // terrain arrays, ba2d, metric terms and base state.
303  // *******************************************************************************************
304  init_stuff(lev, ba, dm, vars_new[lev], vars_old[lev], base_state[lev], z_phys_nd[lev]);
305 
306  //
307  // Note that t_new = time here is elapsed time
308  //
309  t_new[lev] = time;
310  t_old[lev] = time - bogus_large_value;
311 
312  // ********************************************************************************************
313  // Build the data structures for metric quantities used with terrain-fitted coordinates
314  // ********************************************************************************************
315  if ( solverChoice.terrain_type == TerrainType::EB ||
316  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
317  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
318  {
319  const amrex::EB2::IndexSpace& ebis = amrex::EB2::IndexSpace::top();
320  const EB2::Level& eb_level = ebis.getLevel(geom[lev]);
321  if (solverChoice.terrain_type == TerrainType::EB) {
322  eb[lev]->make_all_factories(lev, geom[lev], ba, dm, eb_level);
323  } else if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
324  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
325  eb[lev]->make_cc_factory(lev, geom[lev], ba, dm, eb_level);
326  }
327  }
328  init_zphys(lev, time);
330 
331  //
332  // Make sure that detJ and z_phys_cc are the average of the data on a finer level if there is one
333  // *and* if there is two-way coupling
334  //
335  if ( (SolverChoice::mesh_type != MeshType::ConstantDz) && (solverChoice.coupling_type == CouplingType::TwoWay) ) {
336  for (int crse_lev = lev-1; crse_lev >= 0; crse_lev--) {
337  average_down( *detJ_cc[crse_lev+1], *detJ_cc[crse_lev], 0, 1, refRatio(crse_lev));
338  average_down(*z_phys_cc[crse_lev+1], *z_phys_cc[crse_lev], 0, 1, refRatio(crse_lev));
339  }
340  }
341 
342  // ********************************************************************************************
343  // Build the data structures for canopy model (depends upon z_phys)
344  // ********************************************************************************************
346  m_forest_drag[lev]->define_drag_field(ba, dm, geom[lev], z_phys_cc[lev].get(), z_phys_nd[lev].get());
347  }
348 
349  //********************************************************************************************
350  // Radiation
351  // *******************************************************************************************
352  if (solverChoice.rad_type != RadiationType::None)
353  {
354  rad[lev]->Init(geom[lev], ba, &vars_new[lev][Vars::cons]);
355  }
356 
357  // *****************************************************************************************************
358  // Initialize the boundary conditions (after initializing the terrain but before calling
359  // initHSE or FillCoarsePatch)
360  // *****************************************************************************************************
361  make_physbcs(lev);
362 
363  // ********************************************************************************************
364  // Update the base state at this level by interpolation from coarser level
365  // ********************************************************************************************
366  InterpFromCoarseLevel(base_state[lev], base_state[lev].nGrowVect(),
367  IntVect(0,0,0), // do not fill ghost cells outside the domain
368  base_state[lev-1], 0, 0, base_state[lev].nComp(),
369  geom[lev-1], geom[lev],
370  refRatio(lev-1), &cell_cons_interp,
372 
373  // Impose bc's outside the domain
374  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
375 
376  //********************************************************************************************
377  // Microphysics
378  // *******************************************************************************************
379  int q_size = micro->Get_Qmoist_Size(lev);
380  qmoist[lev].resize(q_size);
381  micro->Define(lev, solverChoice);
382  if (solverChoice.moisture_type != MoistureType::None)
383  {
384  micro->Init(lev, vars_new[lev][Vars::cons],
385  grids[lev], Geom(lev), zero,
386  z_phys_nd[lev], detJ_cc[lev]); // dummy dt value
387  }
388  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
389  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
390  }
391 
392  // ********************************************************************************************
393  // Build the data structures for calculating diffusive/turbulent terms
394  // ********************************************************************************************
395  update_diffusive_arrays(lev, ba, dm);
396 
397  // ********************************************************************************************
398  // Build the data structures for holding sea surface temps and skin temps
399  // ********************************************************************************************
400  sst_lev[lev].resize(1); sst_lev[lev][0] = nullptr;
401  tsk_lev[lev].resize(1); tsk_lev[lev][0] = nullptr;
402 
403  // ********************************************************************************************
404  // Fill data at the new level by interpolation from the coarser level
405  // Note that internal to FillCoarsePatch we will convert velocity to momentum,
406  // then interpolate momentum, then convert momentum back to velocity
407  // Also note that FillCoarsePatch is hard-wired to act only on lev_new at coarse and fine
408  // ********************************************************************************************
409 
410 #ifdef ERF_USE_NETCDF
411  if ( ( (solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid) ) &&
412  !nc_init_file[lev].empty() )
413  {
414  // Just making sure that ghost cells aren't uninitialized...
415  vars_new[lev][Vars::cons].setVal(0.0); vars_old[lev][Vars::cons].setVal(0.0);
416  vars_new[lev][Vars::xvel].setVal(0.0); vars_old[lev][Vars::xvel].setVal(0.0);
417  vars_new[lev][Vars::yvel].setVal(0.0); vars_old[lev][Vars::yvel].setVal(0.0);
418  vars_new[lev][Vars::zvel].setVal(0.0); vars_old[lev][Vars::zvel].setVal(0.0);
419 
420  AMREX_ALWAYS_ASSERT(solverChoice.terrain_type == TerrainType::StaticFittedMesh);
421  if (solverChoice.init_type == InitType::Metgrid) {
422  init_from_metgrid(lev);
423  } else if (solverChoice.init_type == InitType::WRFInput) {
424  init_from_wrfinput(lev, *mf_PSFC[lev]);
425  }
426  init_zphys(lev, time);
428  make_physbcs(lev);
429 
430  dz_min[lev] = (*detJ_cc[lev]).min(0) * geom[lev].CellSize(2);
431 
432  } else {
433 #endif
434  //
435  // Interpolate the solution data
436  //
437  FillCoarsePatch(lev, time);
438 
439  //
440  // Interpolate the 2D arrays at the lower boundary
441  // Note that ba2d is constructed already in init_stuff, but we have not yet defined dmap[lev]
442  // so we must explicitly pass dm.
443  Interp2DArrays(lev,ba2d[lev],dm);
444 
445  // Populate dz_min for dynamically-created fine levels (non-terrain path).
446  if (static_cast<int>(dz_min.size()) <= lev) { dz_min.resize(lev+1); }
447  dz_min[lev] = geom[lev].CellSize(2);
448  if ( SolverChoice::mesh_type != MeshType::ConstantDz && detJ_cc[lev] ) {
449  dz_min[lev] *= (*detJ_cc[lev]).min(0);
450  }
451 #ifdef ERF_USE_NETCDF
452  }
453 #endif
454 
455  // ********************************************************************************************
456  // Initialize the integrator class
457  // ********************************************************************************************
458  dt_mri_ratio[lev] = dt_mri_ratio[lev-1];
460 
461  // ********************************************************************************************
462  // If we are making a new level then the FillPatcher for this level hasn't been allocated yet
463  // ********************************************************************************************
464  if (lev > 0 && cf_width >= 0) {
467  }
468 
469  // ********************************************************************************************
470  // For anelastic levels created from coarse (either on restart or during a run), project the
471  // interpolated velocity to enforce the divergence-free constraint. This Initializes gradp[lev]
472  // via the pressure projection, handling both the pure-anelastic case and the hybrid case
473  // (compressible lev-1, anelastic lev) where there is no coarse gradp to interpolate.
474  // FillPatchers must be constructed above before this call. pp_inc is scratch; zero afterward.
475  // ********************************************************************************************
476  if (solverChoice.anelastic[lev]) {
477  Real dummy_dt = one;
478  project_initial_velocity(lev, time, dummy_dt);
479  pp_inc[lev].setVal(0.0);
480  }
481 
482  //********************************************************************************************
483  // Land Surface Model
484  // *******************************************************************************************
485  int lsm_data_size = lsm.Get_Data_Size();
486  int lsm_flux_size = lsm.Get_Flux_Size();
487  lsm_data[lev].resize(lsm_data_size);
488  lsm_data_name.resize(lsm_data_size);
489  lsm_flux[lev].resize(lsm_flux_size);
490  lsm_flux_name.resize(lsm_flux_size);
491  lsm.Define(lev, solverChoice);
492  if (solverChoice.lsm_type != LandSurfaceType::None)
493  {
494  lsm.Init(lev, vars_new[lev][Vars::cons], Geom(lev), zero); // dummy dt value
495  }
496  for (int mvar(0); mvar<lsm_data[lev].size(); ++mvar) {
497  lsm_data[lev][mvar] = lsm.Get_Data_Ptr(lev,mvar);
498  lsm_data_name[mvar] = lsm.Get_DataName(mvar);
499  }
500  for (int mvar(0); mvar<lsm_flux[lev].size(); ++mvar) {
501  lsm_flux[lev][mvar] = lsm.Get_Flux_Ptr(lev,mvar);
502  lsm_flux_name[mvar] = lsm.Get_FluxName(mvar);
503  }
504 
505  // ********************************************************************************************
506  // Create the SurfaceLayer arrays at this (new) level
507  // ********************************************************************************************
508  if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer) {
509  Vector<MultiFab*> mfv_old = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::xvel],
510  &vars_old[lev][Vars::yvel], &vars_old[lev][Vars::zvel]};
511  m_SurfaceLayer->make_SurfaceLayer_at_level(lev,lev+1,
512  mfv_old, Theta_prim[lev], Qv_prim[lev],
513  Qr_prim[lev], z_phys_nd[lev],
514  Hwave[lev].get(), Lwave[lev].get(), eddyDiffs_lev[lev].get(),
516  sst_lev[lev], tsk_lev[lev], lmask_lev[lev]);
517  }
518 
519  // ********************************************************************************************
520  // Set up the Rayleigh damping vectors at this (new) level
521  // ********************************************************************************************
524  {
526  }
527 
528 }
void update_diffusive_arrays(int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
Definition: ERF_MakeNewArrays.cpp:527
void initialize_integrator(int lev, amrex::MultiFab &cons_mf, amrex::MultiFab &vel_mf)
Definition: ERF_MakeNewArrays.cpp:878
void make_subdomains(const amrex::BoxList &ba, amrex::Vector< amrex::BoxArray > &bins)
Definition: ERF_MakeSubdomains.cpp:6
void update_terrain_arrays(int lev)
Definition: ERF_MakeNewArrays.cpp:861
void init_zphys(int lev, amrex::Real elapsed_time)
Definition: ERF_MakeNewArrays.cpp:686
void init_stuff(int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm, amrex::Vector< amrex::MultiFab > &lev_new, amrex::Vector< amrex::MultiFab > &lev_old, amrex::MultiFab &tmp_base_state, std::unique_ptr< amrex::MultiFab > &tmp_zphys_nd)
Definition: ERF_MakeNewArrays.cpp:24
void Define_ERFFillPatchers(int lev)
Definition: ERF.cpp:2743
int Get_Data_Size()
Definition: ERF_LandSurface.H:101
std::string Get_DataName(const int &varIdx)
Definition: ERF_LandSurface.H:107
std::string Get_FluxName(const int &varIdx)
Definition: ERF_LandSurface.H:113
amrex::MultiFab * Get_Flux_Ptr(const int &lev, const int &varIdx)
Definition: ERF_LandSurface.H:95
void Init(const int &lev, const amrex::MultiFab &cons_in, const amrex::Geometry &geom, const amrex::Real &dt_advance)
Definition: ERF_LandSurface.H:44
void Define(const int &lev, SolverChoice &sc)
Definition: ERF_LandSurface.H:37
int Get_Flux_Size()
Definition: ERF_LandSurface.H:104
Here is the call graph for this function:

◆ MakeNewLevelFromScratch()

void ERF::MakeNewLevelFromScratch ( int  lev,
amrex::Real  time,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
override
27 {
28  //
29  // Note that "time" here is elapsed time
30  //
31  BoxArray ba;
32  DistributionMapping dm;
33  Box domain(Geom(0).Domain());
34  if (lev == 0 && restart_chkfile.empty() &&
35  (max_grid_size[0][0] >= domain.length(0)) &&
36  (max_grid_size[0][1] >= domain.length(1)) &&
37  ba_in.size() != ParallelDescriptor::NProcs())
38  {
39  // We only decompose in z if max_grid_size_z indicates we should
40  bool decompose_in_z = (max_grid_size[0][2] < domain.length(2));
41 
42  ba = ERFPostProcessBaseGrids(Geom(0).Domain(),decompose_in_z);
43  dm = DistributionMapping(ba);
44  } else {
45  ba = ba_in;
46  dm = dm_in;
47  }
48 
49  // ********************************************************************************************
50  // Define grids[lev] to be ba
51  // ********************************************************************************************
52  SetBoxArray(lev, ba);
53 
54  // ********************************************************************************************
55  // Define dmap[lev] to be dm
56  // ********************************************************************************************
57  SetDistributionMap(lev, dm);
58 
59  if (verbose) {
60  amrex::Print() << "BA FROM SCRATCH AT LEVEL " << lev << " " << ba << std::endl;
61  // amrex::Print() <<" SIMPLIFIED BA FROM SCRATCH AT LEVEL " << lev << " " << ba.simplified_list() << std::endl;
62  }
63 
64  subdomains.resize(lev+1);
65  //
66  // Create subdomains at each level within the domain such that
67  // 1) all boxes in a given subdomain are "connected"
68  // 2) no boxes in a subdomain touch any boxes in any other subdomain
69  //
70  make_subdomains(ba.simplified_list(), subdomains[lev]);
71 
72  if (lev == 0) init_bcs();
73 
74  if ( solverChoice.terrain_type == TerrainType::EB ||
75  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
76  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
77  {
78  const amrex::EB2::IndexSpace& ebis = amrex::EB2::IndexSpace::top();
79  const EB2::Level& eb_level = ebis.getLevel(geom[lev]);
80  if (solverChoice.terrain_type == TerrainType::EB) {
81  eb[lev]->make_all_factories(lev, geom[lev], grids[lev], dmap[lev], eb_level);
82  } else if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
83  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
84  eb[lev]->make_cc_factory(lev, geom[lev], grids[lev], dmap[lev], eb_level);
85  }
86  }
87 
88  auto& lev_new = vars_new[lev];
89  auto& lev_old = vars_old[lev];
90 
91  //********************************************************************************************
92  // This allocates all kinds of things, including but not limited to: solution arrays,
93  // terrain arrays, metric terms and base state.
94  // *******************************************************************************************
95  init_stuff(lev, ba, dm, lev_new, lev_old, base_state[lev], z_phys_nd[lev]);
96 
97  //********************************************************************************************
98  // Land Surface Model
99  // *******************************************************************************************
100  int lsm_data_size = lsm.Get_Data_Size();
101  int lsm_flux_size = lsm.Get_Flux_Size();
102  lsm_data[lev].resize(lsm_data_size);
103  lsm_data_name.resize(lsm_data_size);
104  lsm_flux[lev].resize(lsm_flux_size);
105  lsm_flux_name.resize(lsm_flux_size);
106  lsm.Define(lev, solverChoice);
107  if (solverChoice.lsm_type != LandSurfaceType::None)
108  {
109  lsm.Init(lev, vars_new[lev][Vars::cons], Geom(lev), zero); // dummy dt value
110  }
111  for (int mvar(0); mvar<lsm_data[lev].size(); ++mvar) {
112  lsm_data[lev][mvar] = lsm.Get_Data_Ptr(lev,mvar);
113  lsm_data_name[mvar] = lsm.Get_DataName(mvar);
114  }
115  for (int mvar(0); mvar<lsm_flux[lev].size(); ++mvar) {
116  lsm_flux[lev][mvar] = lsm.Get_Flux_Ptr(lev,mvar);
117  lsm_flux_name[mvar] = lsm.Get_FluxName(mvar);
118  }
119 
120 
121 
122  // ********************************************************************************************
123  // Build the data structures for calculating diffusive/turbulent terms
124  // ********************************************************************************************
125  update_diffusive_arrays(lev, ba, dm);
126 
127  // ********************************************************************************************
128  // Build the data structures for holding sea surface temps and skin temps
129  // ********************************************************************************************
130  sst_lev[lev].resize(1); sst_lev[lev][0] = nullptr;
131  tsk_lev[lev].resize(1); tsk_lev[lev][0] = nullptr;
132 
133  // ********************************************************************************************
134  // Thin immersed body
135  // *******************************************************************************************
136  init_thin_body(lev, ba, dm);
137 
138  // ********************************************************************************************
139  // Initialize the integrator class
140  // ********************************************************************************************
141  initialize_integrator(lev, lev_new[Vars::cons],lev_new[Vars::xvel]);
142 
143  // ********************************************************************************************
144  // Initialize the data itself
145  // If (init_type == InitType::WRFInput) then we are initializing terrain and the initial data in
146  // the same call so we must call init_only before update_terrain_arrays
147  // If (init_type != InitType::WRFInput) then we want to initialize the terrain before the initial data
148  // since we may need to use the grid information before constructing
149  // initial idealized data
150  // ********************************************************************************************
151  if (restart_chkfile.empty()) {
152  if ( (solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid) )
153  {
154  AMREX_ALWAYS_ASSERT(solverChoice.terrain_type == TerrainType::StaticFittedMesh);
155  //
156  // Note that "time" here is elapsed time, and start_time is the start_time from wrfinput/metgrid files
157  //
158  init_only(lev, time);
159  init_zphys(lev, time);
161  make_physbcs(lev);
162  } else {
163  //
164  // Note that "time" here is elapsed time, and start_time = 0 when not using wrfinput/metgrid
165  //
166  init_zphys(lev, time);
168  // Note that for init_type != InitType::WRFInput and != InitType::Metgrid,
169  // make_physbcs is called inside init_only
170  init_only(lev, time);
171  }
172  } else {
173  // if restarting and nudging from input sounding, load the input sounding files
174  if (lev == 0 && solverChoice.init_type == InitType::Input_Sounding && solverChoice.nudging_from_input_sounding)
175  {
177  Error("input_sounding file name must be provided via input");
178  }
179 
181 
182  // this will interpolate the input profiles to the nominal height levels
183  // (ranging from 0 to the domain top)
184  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
185  for (int n = 0; n < input_sounding_data.n_sounding_files; n++) {
186  input_sounding_data.read_from_file(geom[lev], zlevels_stag[lev], n, is_moist);
187  }
188 
189  // this will calculate the hydrostatically balanced density and pressure
190  // profiles following WRF ideal.exe
191  if (solverChoice.sounding_type == SoundingType::Ideal) {
193  } else if (solverChoice.sounding_type == SoundingType::Isentropic ||
194  solverChoice.sounding_type == SoundingType::DryIsentropic) {
195  input_sounding_data.assume_dry = (solverChoice.sounding_type == SoundingType::DryIsentropic);
197  }
198  }
199 
200  // We re-create terrain_blanking on restart rather than storing it in the checkpoint
201  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
202  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
203  int ngrow = ComputeGhostCells(solverChoice) + 2;
204  terrain_blanking[lev]->setVal(1.0);
205  MultiFab::Subtract(*terrain_blanking[lev], EBFactory(lev).getVolFrac(), 0, 0, 1, ngrow);
206  terrain_blanking[lev]->FillBoundary(geom[lev].periodicity());
207  }
208  }
209 
210  // Read in tables needed for windfarm simulations
211  // fill in Nturb multifab - number of turbines in each mesh cell
212  // write out the vtk files for wind turbine location and/or
213  // actuator disks
214  #ifdef ERF_USE_WINDFARM
215  init_windfarm(lev);
216  #endif
217 
218  // ********************************************************************************************
219  // Build the data structures for canopy model (depends upon z_phys)
220  // ********************************************************************************************
221  if (restart_chkfile.empty()) {
223  m_forest_drag[lev]->define_drag_field(ba, dm, geom[lev], z_phys_cc[lev].get(), z_phys_nd[lev].get());
224  }
225  }
226 
227  //********************************************************************************************
228  // Microphysics
229  // *******************************************************************************************
230  int q_size = micro->Get_Qmoist_Size(lev);
231  qmoist[lev].resize(q_size);
232  micro->Define(lev, solverChoice);
233  if (solverChoice.moisture_type != MoistureType::None)
234  {
235  micro->Init(lev, vars_new[lev][Vars::cons],
236  grids[lev], Geom(lev), zero,
237  z_phys_nd[lev], detJ_cc[lev]); // dummy dt value
238  }
239  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
240  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
241  }
242 
243  //********************************************************************************************
244  // Radiation
245  // *******************************************************************************************
246  if (solverChoice.rad_type != RadiationType::None)
247  {
248  rad[lev]->Init(geom[lev], ba, &vars_new[lev][Vars::cons]);
249  }
250 
251  // ********************************************************************************************
252  // If we are making a new level then the FillPatcher for this level hasn't been allocated yet
253  // ********************************************************************************************
254  if (lev > 0 && cf_width >= 0) {
257  }
258 
259 #ifdef ERF_USE_PARTICLES
260  if (restart_chkfile.empty()) {
261  if (lev == 0) {
262  initializeTracers((ParGDBBase*)GetParGDB(),z_phys_nd,time);
263  }
264  // For lev > 0: particle redistribute is handled in timeStep() AFTER
265  // regrid() completes, not here inside MakeNewLevelFromCoarse.
266  }
267 #endif
268 }
BoxArray ERFPostProcessBaseGrids(const Box &domain, bool decompose_in_z)
Definition: ERF_ChopGrids.cpp:6
void init_only(int lev, amrex::Real time)
Definition: ERF.cpp:1822
void init_thin_body(int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
Definition: ERF_MakeNewLevel.cpp:898
bool nudging_from_input_sounding
Definition: ERF_DataStruct.H:1326
Here is the call graph for this function:

◆ MakeVTKFilename()

std::string ERF::MakeVTKFilename ( int  nstep)
11  {
12  // Ensure output directory exists
13  const std::string dir = "Output_StormTracker";
14  if (!fs::exists(dir)) {
15  fs::create_directory(dir);
16  }
17 
18  std::ostringstream oss;
19  oss << dir << "/storm_track_" << std::setw(7) << std::setfill('0') << nstep << ".vtk";
20  return oss.str();
21 }

◆ MakeVTKFilename_EyeTracker_xy()

std::string ERF::MakeVTKFilename_EyeTracker_xy ( int  nstep)
38  {
39  // Ensure output directory exists
40  const std::string dir = "Output_StormTracker/xy";
41  if (!fs::exists(dir)) {
42  fs::create_directories(dir);
43  }
44 
45  // Construct filename with zero-padded step
46  std::ostringstream oss;
47  oss << dir << "/storm_track_xy_" << std::setw(7) << std::setfill('0') << nstep << ".vtk";
48  return oss.str();
49 }

◆ MakeVTKFilename_TrackerCircle()

std::string ERF::MakeVTKFilename_TrackerCircle ( int  nstep)
24  {
25  // Ensure output directory exists
26  const std::string dir = "Output_StormTracker/tracker_circle";
27  if (!fs::exists(dir)) {
28  fs::create_directories(dir);
29  }
30 
31  // Construct filename with zero-padded step
32  std::ostringstream oss;
33  oss << dir << "/storm_tracker_circle_" << std::setw(7) << std::setfill('0') << nstep << ".vtk";
34  return oss.str();
35 }

◆ nghost_eb_basic()

static int ERF::nghost_eb_basic ( )
inlinestaticprivate
1744  { return 5; }

◆ nghost_eb_full()

static int ERF::nghost_eb_full ( )
inlinestaticprivate
1751  { return 4; }

◆ nghost_eb_volume()

static int ERF::nghost_eb_volume ( )
inlinestaticprivate
1748  { return 5; }

◆ NumDataLogs()

AMREX_FORCE_INLINE int ERF::NumDataLogs ( )
inlineprivatenoexcept
1540  {
1541  return static_cast<int>(datalog.size());
1542  }

◆ NumDerDataLogs()

AMREX_FORCE_INLINE int ERF::NumDerDataLogs ( )
inlineprivatenoexcept
1547  {
1548  return static_cast<int>(der_datalog.size());
1549  }

◆ NumSampleLineLogs()

AMREX_FORCE_INLINE int ERF::NumSampleLineLogs ( )
inlineprivatenoexcept
1576  {
1577  return static_cast<int>(samplelinelog.size());
1578  }

◆ NumSampleLines()

AMREX_FORCE_INLINE int ERF::NumSampleLines ( )
inlineprivatenoexcept
1602  {
1603  return static_cast<int>(sampleline.size());
1604  }

◆ NumSamplePointLogs()

AMREX_FORCE_INLINE int ERF::NumSamplePointLogs ( )
inlineprivatenoexcept
1562  {
1563  return static_cast<int>(sampleptlog.size());
1564  }

◆ NumSamplePoints()

AMREX_FORCE_INLINE int ERF::NumSamplePoints ( )
inlineprivatenoexcept
1589  {
1590  return static_cast<int>(samplepoint.size());
1591  }

◆ operator=() [1/2]

ERF& ERF::operator= ( const ERF other)
delete

◆ operator=() [2/2]

ERF& ERF::operator= ( ERF &&  other)
deletenoexcept

◆ PackAtmosphericStates()

void ERF::PackAtmosphericStates ( amrex::Vector< amrex::MultiFab * > &  states,
amrex::Real  time 
)
63 {
64  using namespace amrex;
65 
66  // Contract slot indices (mirrors ERFRemoraCouplingContract.H; repeated here
67  // to avoid a driver→submodule header dependency).
68  constexpr int iUwind = 0, iVwind = 1, iPatm = 2, iRH = 3, iTair = 4;
69  constexpr int iCloud = 5, iRain = 6, iSWrad = 7, iLWrad = 8;
70 
71  const int lev = 0;
72 
73  auto& cons = vars_new[lev][Vars::cons];
74  auto& xvel = vars_new[lev][Vars::xvel]; // XFace
75  auto& yvel = vars_new[lev][Vars::yvel]; // YFace
76 
77  const bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
78  const bool has_radiation = (!rad_fluxes.empty() && rad_fluxes[lev] != nullptr);
79 
80  amrex::ignore_unused(has_moisture, has_radiation);
81 
82  const auto& ba = cons.boxArray();
83  const auto& dm = cons.DistributionMap();
84  const auto& ba2d_lev = ba2d[lev];
85 
86  // --- Uwind + Vwind: use AMReX's average_face_to_cellcenter which correctly
87  // handles tile boundaries via growntilebox(1) internally. ---
88  if ((iUwind < static_cast<int>(states.size()) && states[iUwind] != nullptr) ||
89  (iVwind < static_cast<int>(states.size()) && states[iVwind] != nullptr)) {
90 
91  auto& zvel = vars_new[lev][Vars::zvel];
92  MultiFab cc_vel(ba, dm, AMREX_SPACEDIM, 0);
93  amrex::average_face_to_cellcenter(cc_vel, 0,
94  Array<const MultiFab*, AMREX_SPACEDIM>{&xvel, &yvel, &zvel});
95 
96  // Collapse to 2D slab
97  MultiFab uv_slab(ba2d_lev, dm, 2, 0); // comp0=u, comp1=v
98  uv_slab.ParallelCopy(cc_vel, 0, 0, 2);
99 
100  if (iUwind < static_cast<int>(states.size()) && states[iUwind] != nullptr) {
101  MultiFab u_alias(uv_slab, amrex::make_alias, 0, 1); // alias u component
102  IntVect ratio = ba2d_lev.minimalBox().length()
103  / states[iUwind]->boxArray().minimalBox().length();
104  amrex::average_down(u_alias, *states[iUwind], 0, 1, ratio);
105  }
106 
107  if (iVwind < static_cast<int>(states.size()) && states[iVwind] != nullptr) {
108  MultiFab v_alias(uv_slab, amrex::make_alias, 1, 1); // alias v component
109  IntVect ratio = ba2d_lev.minimalBox().length()
110  / states[iVwind]->boxArray().minimalBox().length();
111  amrex::average_down(v_alias, *states[iVwind], 0, 1, ratio);
112  }
113  }
114 
115  // --- Patm: getPgivenRTh(RhoTheta, qv) at k=0 ---
116  if (iPatm < static_cast<int>(states.size()) && states[iPatm] != nullptr) {
117  MultiFab tmp(ba2d_lev, dm, 1, 0);
118  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
119  Box bx = mfi.tilebox();
120  auto const& c = cons.const_array(mfi);
121  auto t = tmp.array(mfi);
122  if (has_moisture) {
123  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
124  const Real qv = c(i,j,k,RhoQ1_comp) / c(i,j,k,Rho_comp);
125  t(i,j,k) = getPgivenRTh(c(i,j,k,RhoTheta_comp), qv);
126  });
127  } else {
128  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
129  t(i,j,k) = getPgivenRTh(c(i,j,k,RhoTheta_comp));
130  });
131  }
132  }
133  IntVect ratio = ba2d_lev.minimalBox().length() / states[iPatm]->boxArray().minimalBox().length();
134  amrex::average_down(tmp, *states[iPatm], 0, 1, ratio);
135  }
136 
137  // --- Tair: getTgivenRandRTh(rho, RhoTheta, qv) at k=0 [K] ---
138  if (iTair < static_cast<int>(states.size()) && states[iTair] != nullptr) {
139  MultiFab tmp(ba2d_lev, dm, 1, 0);
140  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
141  Box bx = mfi.tilebox();
142  auto const& c = cons.const_array(mfi);
143  auto t = tmp.array(mfi);
144  if (has_moisture) {
145  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
146  const Real qv = c(i,j,k,RhoQ1_comp) / c(i,j,k,Rho_comp);
147  t(i,j,k) = getTgivenRandRTh(c(i,j,k,Rho_comp), c(i,j,k,RhoTheta_comp), qv);
148  });
149  } else {
150  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
151  t(i,j,k) = getTgivenRandRTh(c(i,j,k,Rho_comp), c(i,j,k,RhoTheta_comp));
152  });
153  }
154  }
155  IntVect ratio = ba2d_lev.minimalBox().length() / states[iTair]->boxArray().minimalBox().length();
156  amrex::average_down(tmp, *states[iTair], 0, 1, ratio);
157  }
158 
159  // --- Humidity lane: export relative humidity [0-1] for REMORA bulk fluxes ---
160  if (has_moisture) {
161 #if 0
162  if (iRH < static_cast<int>(states.size()) && states[iRH] != nullptr) {
163  MultiFab tmp(ba2d_lev, dm, 1, 0);
164  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
165  Box bx = mfi.tilebox();
166  auto const& c = cons.const_array(mfi);
167  auto t = tmp.array(mfi);
168  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
169  const Real qv = c(i,j,k,RhoQ1_comp) / c(i,j,k,Rho_comp);
170  const Real p_pa = getPgivenRTh(c(i,j,k,RhoTheta_comp), qv);
171  const Real temp = getTgivenRandRTh(c(i,j,k,Rho_comp), c(i,j,k,RhoTheta_comp), qv);
172  Real qsat = Real(0.0);
173  erf_qsatw(temp, p_pa * Real(0.01), qsat);
174  t(i,j,k) = amrex::max(Real(0.0), amrex::min(Real(1.0),
175  qv / amrex::max(qsat, Real(1.0e-12))));
176  });
177  }
178  IntVect ratio = ba2d_lev.minimalBox().length() / states[iRH]->boxArray().minimalBox().length();
179  amrex::average_down(tmp, *states[iRH], 0, 1, ratio);
180  }
181 #else
182  amrex::ignore_unused(iRH);
183  // Avoiding unexercised moist assumptions: retain the driver-prefilled
184  // humidity lane until live ERF->REMORA Qair semantics are made explicit.
185 #endif
186  if (iCloud < static_cast<int>(states.size()) && states[iCloud] != nullptr) {
187  const int qc_idx = solverChoice.moisture_indices.qc;
188  const int qi_idx = solverChoice.moisture_indices.qi;
189  if (qc_idx != -1 || qi_idx != -1) {
190  MultiFab tmp(ba2d_lev, dm, 1, 0);
191  const Real cf = amrex::max(Real(0.0), amrex::min(Real(1.0), cloud_fraction(0.0)));
192  amrex::ignore_unused(cf); // keep diagnostic computation active for consistency with ERF scalar stats
193  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
194  Box bx = mfi.tilebox();
195  auto const& c = cons.const_array(mfi);
196  auto t = tmp.array(mfi);
197  const int klo = ba.minimalBox().smallEnd(2);
198  const int khi = ba.minimalBox().bigEnd(2);
199  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
200  int cloudy = 0;
201  for (int kk = klo; kk <= khi; ++kk) {
202  const Real rho = c(i,j,kk,Rho_comp);
203  const Real qc = (qc_idx != -1) ? c(i,j,kk,qc_idx) / rho : Real(0.0);
204  const Real qi = (qi_idx != -1) ? c(i,j,kk,qi_idx) / rho : Real(0.0);
205  if (qc + qi > Real(0.0)) { cloudy = 1; break; }
206  }
207  t(i,j,k) = static_cast<Real>(cloudy);
208  });
209  }
210  IntVect ratio = ba2d_lev.minimalBox().length() / states[iCloud]->boxArray().minimalBox().length();
211  amrex::average_down(tmp, *states[iCloud], 0, 1, ratio);
212  }
213  }
214 #if 0
215  if (iRain < static_cast<int>(states.size()) && states[iRain] != nullptr) {
216  int qr_idx = solverChoice.moisture_indices.qr;
217  if (qr_idx != -1) {
218  MultiFab tmp(ba2d_lev, dm, 1, 0);
219  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
220  Box bx = mfi.tilebox();
221  auto const& c = cons.const_array(mfi);
222  auto t = tmp.array(mfi);
223  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
224  t(i,j,k) = c(i,j,k,qr_idx) / c(i,j,k,Rho_comp);
225  });
226  }
227  IntVect ratio = ba2d_lev.minimalBox().length() / states[iRain]->boxArray().minimalBox().length();
228  amrex::average_down(tmp, *states[iRain], 0, 1, ratio);
229  }
230  }
231 #else
232  amrex::ignore_unused(iRain);
233  // Avoiding unexercised moist assumptions: retain the driver-prefilled
234  // rain lane until live ERF->REMORA rain semantics are made explicit.
235 #endif
236  }
237  // No moisture: leave RH/Cloud/Rain slabs at their driver-pre-filled values.
238 
239  // --- Radiation: sw_flux_dn (comp=1) and lw_flux_dn (comp=3) from rad_fluxes ---
240  // When absent, leave slabs at their driver-pre-filled values.
241  if (has_radiation) {
242  if (iSWrad < static_cast<int>(states.size()) && states[iSWrad] != nullptr) {
243  MultiFab tmp(ba2d_lev, dm, 1, 0);
244  tmp.ParallelCopy(*rad_fluxes[lev], 1, 0, 1);
245  IntVect ratio = ba2d_lev.minimalBox().length() / states[iSWrad]->boxArray().minimalBox().length();
246  amrex::average_down(tmp, *states[iSWrad], 0, 1, ratio);
247  }
248  if (iLWrad < static_cast<int>(states.size()) && states[iLWrad] != nullptr) {
249  MultiFab tmp(ba2d_lev, dm, 1, 0);
250  tmp.ParallelCopy(*rad_fluxes[lev], 3, 0, 1);
251  IntVect ratio = ba2d_lev.minimalBox().length() / states[iLWrad]->boxArray().minimalBox().length();
252  amrex::average_down(tmp, *states[iLWrad], 0, 1, ratio);
253  }
254  }
255 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:228
amrex::Real cloud_fraction(double time)
Definition: ERF_WriteScalarProfiles.cpp:452
@ qi
Definition: ERF_WSM6.H:26
Definition: ERF_ConsoleIO.cpp:12
int qi
Definition: ERF_DataStruct.H:109
Here is the call graph for this function:

◆ ParameterSanityChecks()

void ERF::ParameterSanityChecks ( )
private
2498 {
2500 
2501  // We don't allow use_real_bcs to be true if init_type is not either InitType::WRFInput or InitType::Metgrid
2503  ((solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid)) );
2504 
2506 
2507  if (cf_set_width != 0) {
2508  Abort("You must set cf_set_width == 0");
2509  }
2510  if (cf_width < 0 || cf_set_width < 0 || cf_width < cf_set_width) {
2511  Abort("You must set cf_width >= cf_set_width >= 0");
2512  }
2513  if (max_level > 0 && cf_set_width > 0) {
2514  for (int lev = 1; lev <= max_level; lev++) {
2515  if (cf_set_width%ref_ratio[lev-1][0] != 0 ||
2516  cf_set_width%ref_ratio[lev-1][1] != 0 ||
2517  cf_set_width%ref_ratio[lev-1][2] != 0 ) {
2518  Abort("You must set cf_width to be a multiple of ref_ratio");
2519  }
2520  }
2521  }
2522 
2523  // If fixed_mri_dt_ratio is set, it must be even
2524  if (fixed_mri_dt_ratio > 0 && (fixed_mri_dt_ratio%2 != 0) )
2525  {
2526  Abort("If you specify fixed_mri_dt_ratio, it must be even");
2527  }
2528 
2529  for (int lev = 0; lev <= max_level; lev++)
2530  {
2531  // We ignore fixed_fast_dt if not substepping
2532  if (solverChoice.substepping_type[lev] == SubsteppingType::None) {
2533  fixed_fast_dt[lev] = -one;
2534  }
2535 
2536  // If both fixed_dt and fast_dt are specified, their ratio must be an even integer
2537  if (fixed_dt[lev] > zero && fixed_fast_dt[lev] > zero && fixed_mri_dt_ratio <= 0)
2538  {
2539  Real eps = Real(1.e-12);
2540  int ratio = static_cast<int>( ( (one+eps) * fixed_dt[lev] ) / fixed_fast_dt[lev] );
2541  if (fixed_dt[lev] / fixed_fast_dt[lev] != ratio)
2542  {
2543  Abort("Ratio of fixed_dt to fixed_fast_dt must be an even integer");
2544  }
2545  }
2546 
2547  // If all three are specified, they must be consistent
2548  if (fixed_dt[lev] > zero && fixed_fast_dt[lev] > zero && fixed_mri_dt_ratio > 0)
2549  {
2550  if (fixed_dt[lev] / fixed_fast_dt[lev] != fixed_mri_dt_ratio)
2551  {
2552  Abort("Dt is over-specfied");
2553  }
2554  }
2555  } // lev
2556 
2557  if (solverChoice.coupling_type == CouplingType::TwoWay && cf_width > 0) {
2558  Abort("For two-way coupling you must set cf_width = 0");
2559  }
2560 }
Here is the call graph for this function:

◆ PerformDataAssimilation()

void ERF::PerformDataAssimilation ( int  da_iter)

◆ PlotFileName()

std::string ERF::PlotFileName ( int  lev) const
private

◆ PlotFileVarNames()

Vector< std::string > ERF::PlotFileVarNames ( amrex::Vector< std::string >  plot_var_names)
staticprivate
254 {
255  Vector<std::string> names;
256 
257  names.insert(names.end(), plot_var_names.begin(), plot_var_names.end());
258 
259  return names;
260 
261 }

◆ poisson_wall_dist()

void ERF::poisson_wall_dist ( int  lev)

Calculate wall distances using the Poisson equation

The zlo boundary is assumed to correspond to the land surface. If there are no boundary walls, then the other use case is to calculate wall distances for immersed boundaries (embedded or thin body).

See Tucker, P. G. (2003). Differential equation-based wall distance computation for DES and RANS. Journal of Computational Physics, 190(1), 229–Real(248.) https://doi.org/Real(10.1016)/S0021-9991(03)00272-9

23 {
24  BL_PROFILE("ERF::poisson_wall_dist()");
25 
26  bool havewall{false};
27  Orientation zlo(Direction::z, Orientation::low);
28  if ( ( phys_bc_type[zlo] == ERF_BC::surface_layer ) ||
29  ( phys_bc_type[zlo] == ERF_BC::no_slip_wall ) )/*||
30  ((phys_bc_type[zlo] == ERF_BC::slip_wall) && (dom_hi.z > dom_lo.z)) )*/
31  {
32  havewall = true;
33  }
34 
35  auto const& geomdata = geom[lev];
36  auto const& dxinv = geomdata.InvCellSizeArray();
37 
38  auto const& zphys_arr = z_phys_nd[lev]->const_arrays();
39 
40  if (havewall) {
41 #if 1
42  // Bypass wall dist calc in the trivial cases
43 
44  if (solverChoice.mesh_type == MeshType::ConstantDz) {
45  Print() << "Directly calculating direct wall distance for constant dz" << std::endl;
46  const Real* prob_lo = geomdata.ProbLo();
47  const Real* dx = geomdata.CellSize();
48  for (MFIter mfi(*walldist[lev]); mfi.isValid(); ++mfi) {
49  const Box& bx = mfi.validbox();
50  auto dist_arr = walldist[lev]->array(mfi);
51  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
52  dist_arr(i, j, k) = prob_lo[2] + (k + myhalf) * dx[2];
53  });
54  }
55  return;
56  }
57 
58  if (solverChoice.mesh_type == MeshType::StretchedDz) {
59  Print() << "Directly calculating direct wall distance for stretched dz" << std::endl;
60  for (MFIter mfi(*walldist[lev],TileNoZ()); mfi.isValid(); ++mfi) {
61  const Box& bx = mfi.validbox();
62  auto dist_arr = walldist[lev]->array(mfi);
63  const auto zcc_arr = z_phys_cc[lev]->const_array(mfi);
64  const auto znd_arr = z_phys_nd[lev]->const_array(mfi);
65  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
66  dist_arr(i, j, k) = zcc_arr(i, j, k) - znd_arr(i, j, 0);
67  });
68  }
69  return;
70  }
71 #endif
72  }
73  else
74  {
75  Error("No solid boundaries in the computational domain");
76  }
77 
78  Print() << "Calculating Poisson wall distance for general terrain" << std::endl;
79 
80  // Make sure the solver only sees the levels over which we are solving
81  Vector<Geometry> geom_tmp; geom_tmp.push_back(geom[lev]);
82  Vector<BoxArray> ba_tmp; ba_tmp.push_back(walldist[lev]->boxArray());
83  Vector<DistributionMapping> dm_tmp; dm_tmp.push_back(walldist[lev]->DistributionMap());
84 
85  Vector<MultiFab> rhs;
86  Vector<MultiFab> phi;
87 
88  if (solverChoice.terrain_type == TerrainType::EB) {
89  amrex::Error("Wall dist calc not implemented for EB");
90  } else {
91  rhs.resize(1); rhs[0].define(ba_tmp[0], dm_tmp[0], 1, 0);
92  phi.resize(1); phi[0].define(ba_tmp[0], dm_tmp[0], 1, 1);
93  }
94 
95  rhs[0].setVal(-1.0);
96 
97  auto const dom_lo = lbound(geom[lev].Domain());
98  auto const dom_hi = ubound(geom[lev].Domain());
99 
100  // ****************************************************************************
101  // Initialize phi
102  // (It is essential that we do this in order to fill the corners; this is
103  // used if we include blanking.)
104  // ****************************************************************************
105  phi[0].setVal(0.0);
106 
107  // ****************************************************************************
108  // Interior boundaries are marked with phi=0
109  // ****************************************************************************
110 #if 0
111  // Define an overset mask (0 or 1) to set dirichlet nodes on walls
112  // 1 means the node is an unknown. 0 means it's known.
113  iMultiFab mask(ba_tmp[0], dm_tmp[0], 1, 0);
114  Vector<const iMultiFab*> overset_mask = {&mask};
115 
116  mask.setVal(1);
118  Warning("Poisson distance is inaccurate for bodies in open domains that are small compared to the domain size, skipping");
119  return;
120 
121  Gpu::DeviceVector<IntVect> xfacelist, yfacelist, zfacelist;
122 
123  xfacelist.resize(solverChoice.advChoice.zero_xflux.size());
124  yfacelist.resize(solverChoice.advChoice.zero_yflux.size());
125  zfacelist.resize(solverChoice.advChoice.zero_zflux.size());
126 
127  if (xfacelist.size() > 0) {
128  Gpu::copy(amrex::Gpu::hostToDevice,
131  xfacelist.begin());
132  Print() << " masking interior xfaces" << std::endl;
133  }
134  if (yfacelist.size() > 0) {
135  Gpu::copy(amrex::Gpu::hostToDevice,
138  yfacelist.begin());
139  Print() << " masking interior yfaces" << std::endl;
140  }
141  if (zfacelist.size() > 0) {
142  Gpu::copy(amrex::Gpu::hostToDevice,
145  zfacelist.begin());
146  Print() << " masking interior zfaces" << std::endl;
147  }
148 
149  for (MFIter mfi(phi[0]); mfi.isValid(); ++mfi) {
150  const Box& bx = mfi.validbox();
151 
152  auto phi_arr = phi[0].array(mfi);
153  auto mask_arr = mask.array(mfi);
154 
155  if (xfacelist.size() > 0) {
156  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
157  for (int iface=0; iface < xfacelist.size(); ++iface) {
158  if ((i == xfacelist[iface][0]) &&
159  (j == xfacelist[iface][1]) &&
160  (k == xfacelist[iface][2]))
161  {
162  mask_arr(i, j , k ) = 0;
163  mask_arr(i, j , k+1) = 0;
164  mask_arr(i, j+1, k ) = 0;
165  mask_arr(i, j+1, k+1) = 0;
166  }
167  }
168  });
169  }
170 
171  if (yfacelist.size() > 0) {
172  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
173  for (int iface=0; iface < yfacelist.size(); ++iface) {
174  if ((i == yfacelist[iface][0]) &&
175  (j == yfacelist[iface][1]) &&
176  (k == yfacelist[iface][2]))
177  {
178  mask_arr(i , j, k ) = 0;
179  mask_arr(i , j, k+1) = 0;
180  mask_arr(i+1, j, k ) = 0;
181  mask_arr(i+1, j, k+1) = 0;
182  }
183  }
184  });
185  }
186 
187  if (zfacelist.size() > 0) {
188  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
189  for (int iface=0; iface < zfacelist.size(); ++iface) {
190  if ((i == xfacelist[iface][0]) &&
191  (j == xfacelist[iface][1]) &&
192  (k == xfacelist[iface][2]))
193  {
194  mask_arr(i , j , k) = 0;
195  mask_arr(i , j+1, k) = 0;
196  mask_arr(i+1, j , k) = 0;
197  mask_arr(i+1, j+1, k) = 0;
198  }
199  }
200  });
201  }
202  }
203  }
204 #endif
205 
206  // ****************************************************************************
207  // Setup BCs, with solid domain boundaries being dirichlet
208  // ****************************************************************************
209  amrex::Array<amrex::LinOpBCType,AMREX_SPACEDIM> bc3d_lo, bc3d_hi;
210  for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
211  if (geom[0].isPeriodic(dir)) {
212  bc3d_lo[dir] = LinOpBCType::Periodic;
213  bc3d_hi[dir] = LinOpBCType::Periodic;
214  } else {
215  bc3d_lo[dir] = LinOpBCType::Neumann;
216  bc3d_hi[dir] = LinOpBCType::Neumann;
217  }
218  }
219  if (havewall) {
220  Print() << " Poisson zlo BC is dirichlet" << std::endl;
221  bc3d_lo[2] = LinOpBCType::Dirichlet;
222  }
223  Print() << " bc lo : " << bc3d_lo << std::endl;
224  Print() << " bc hi : " << bc3d_hi << std::endl;
225 
226  if (!solverChoice.advChoice.have_zero_flux_faces && !havewall) {
227  Error("No solid boundaries in the computational domain");
228  }
229 
230  LPInfo info; // defaults
231 
232 /* Nodal solver cannot have hidden dimensions */
233 #if 0
234  // Allow a hidden direction if the domain is one cell wide
235  if (dom_lo.x == dom_hi.x) {
236  info.setHiddenDirection(0);
237  Print() << " domain is 2D in yz" << std::endl;
238  } else if (dom_lo.y == dom_hi.y) {
239  info.setHiddenDirection(1);
240  Print() << " domain is 2D in xz" << std::endl;
241  } else if (dom_lo.z == dom_hi.z) {
242  info.setHiddenDirection(2);
243  Print() << " domain is 2D in xy" << std::endl;
244  }
245 #endif
246 
247 #if 0
248  Vector<EBFArrayBoxFactory const*> factory_vec;
249  factory_vec.push_back(static_cast<FabFactory<FArrayBox> const*>(&EBFactory(lev));
250 #endif
251 
252  // ****************************************************************************
253  // Setup Poisson problem
254  // (A \alpha - B \nabla \cdot \beta \nabla ) \phi = f
255  //
256  // In physical space:
257  // \nabla \cdot \nabla \phi = -1
258  //
259  // In computational space:
260  // grad(phi) = T^T \nabla \phi
261  // and
262  // \nabla \cdot (h_zeta T (T^T \nabla \phi)) = -h_zeta
263  // where T = inv(J), T^T is the transpose of inv(J)
264  // ****************************************************************************
265  constexpr Real constA = zero;
266  constexpr Real constB = -one;
267 
268  MLABecLaplacian mlabec(geom_tmp, ba_tmp, dm_tmp, info);
269 
270  mlabec.setScalars(constA, constB);
271  mlabec.setACoeffs(0, zero);
272 #if 1
273  // Set beta coefficients at faces
274  Array<MultiFab, AMREX_SPACEDIM> beta;
275 
276  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
277  BoxArray ba_face = ba_tmp[0];
278  ba_face.surroundingNodes(idim); // Convert to face-centered in direction idim
279  beta[idim].define(ba_face, dm_tmp[0], 1, 0);
280  }
281 
282  auto beta0_arr = beta[0].arrays();
283  auto beta1_arr = beta[1].arrays();
284  auto beta2_arr = beta[2].arrays();
285 
286  // Note: This ignores the off-diagonal components of (h_zeta T T^T), which
287  // is equivalent to assuming that h_xi and h_eta are small.
288 
289  ParallelFor(beta[0], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
290  beta0_arr[b](i, j, k) = Compute_h_zeta_AtIface(i, j, k, dxinv, zphys_arr[b]);;
291  });
292  ParallelFor(beta[1], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
293  beta1_arr[b](i, j, k) = Compute_h_zeta_AtJface(i, j, k, dxinv, zphys_arr[b]);;
294  });
295  ParallelFor(beta[2], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
296  Real inv_h_zeta = one / Compute_h_zeta_AtKface(i, j, k, dxinv, zphys_arr[b]);
297  Real h_xi = Compute_h_xi_AtKface(i, j, k, dxinv, zphys_arr[b]);
298  Real h_eta = Compute_h_eta_AtKface(i, j, k, dxinv, zphys_arr[b]);
299  beta2_arr[b](i, j, k) = inv_h_zeta * (1 + h_xi*h_xi + h_eta*h_eta);
300  });
301 
302  mlabec.setBCoeffs(0, GetArrOfConstPtrs(beta));
303 
304  // Set RHS := -h_zeta
305  auto rhs_arr = rhs[0].arrays();
306  ParallelFor(rhs[0], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
307  rhs_arr[b](i, j, k) = -Compute_h_zeta_AtCellCenter(i, j, k, dxinv, zphys_arr[b]);
308  });
309 #else
310  mlabec.setBCoeffs(0, one);
311 #endif
312 
313  mlabec.setDomainBC(bc3d_lo, bc3d_hi);
314 
315  if (lev > 0) {
316  mlabec.setCoarseFineBC(nullptr, ref_ratio[lev-1], LinOpBCType::Neumann);
317  }
318 
319  // If we have inhomogeneous BCs -- do this after setCoarseFineBC
320  mlabec.setLevelBC(0, nullptr);
321 
322  // ****************************************************************************
323  // Solve Poisson problem with MLMG
324  // ****************************************************************************
325  const Real reltol = solverChoice.poisson_reltol;
326  const Real abstol = solverChoice.poisson_abstol;
327  const int n_corr = solverChoice.ncorr;
328  constexpr int max_iter = 100;
329 
330  MLMG mlmg(mlabec);
331  mlmg.setMaxIter(max_iter);
332  mlmg.setVerbose(mg_verbose);
333  mlmg.setBottomVerbose(0);
334 
335  for (int icorr=0; icorr <= n_corr; ++icorr) {
336  Print()<< "Solving wall distance poisson, icorr=" << icorr << std::endl;
337 
338  mlmg.solve(GetVecOfPtrs(phi),
339  GetVecOfConstPtrs(rhs),
340  reltol, abstol);
341 
342  // ****************************************************************************
343  // Apply BCs: dirichlet (odd) on zlo, neumann (even) / periodic elsewhere
344  // ****************************************************************************
345 
346  // Overwrite with periodic fill outside domain and fine-fine fill inside
347  phi[0].FillBoundary(geom[lev].periodicity());
348 
349  if (!geom[lev].isPeriodic(0)) {
350  for (MFIter mfi(phi[0],true); mfi.isValid(); ++mfi)
351  {
352  Box bx = mfi.tilebox();
353  const Array4<Real>& phi_arr = phi[0].array(mfi);
354  if (bx.smallEnd(0) <= dom_lo.x) {
355  ParallelFor(makeSlab(bx,0,dom_lo.x),
356  [=] AMREX_GPU_DEVICE (int i, int j, int k)
357  {
358  phi_arr(i-1,j,k) = phi_arr(i,j,k); // even BC
359  });
360  } // lo x
361  if (bx.bigEnd(0) >= dom_hi.x) {
362  ParallelFor(makeSlab(bx,0,dom_hi.x),
363  [=] AMREX_GPU_DEVICE (int i, int j, int k)
364  {
365  phi_arr(i+1,j,k) = phi_arr(i,j,k); // even BC
366  });
367  } // hi x
368  } // mfi
369  } // not periodic in x
370 
371  if (!geom[lev].isPeriodic(1)) {
372  for (MFIter mfi(phi[0],true); mfi.isValid(); ++mfi)
373  {
374  Box bx = mfi.tilebox();
375  Box bx2(bx); bx2.grow(0,1);
376  const Array4<Real>& phi_arr = phi[0].array(mfi);
377  if (bx.smallEnd(1) <= dom_lo.y) {
378  ParallelFor(makeSlab(bx2,1,dom_lo.y),
379  [=] AMREX_GPU_DEVICE (int i, int j, int k)
380  {
381  phi_arr(i,j-1,k) = phi_arr(i,j,k); // even BC
382  });
383  } // lo y
384  if (bx.bigEnd(1) >= dom_hi.y) {
385  ParallelFor(makeSlab(bx2,1,dom_hi.y),
386  [=] AMREX_GPU_DEVICE (int i, int j, int k)
387  {
388  phi_arr(i,j+1,k) = phi_arr(i,j,k); // even BC
389  });
390  } // hi y
391 
392  } // mfi
393  } // not periodic in y
394 
395  for (MFIter mfi(phi[0],true); mfi.isValid(); ++mfi)
396  {
397  Box bx = mfi.tilebox();
398  Box bx3(bx); bx3.grow(0,1); bx3.grow(1,1);
399  const Array4<Real>& phi_arr = phi[0].array(mfi);
400  if (bx.smallEnd(2) <= dom_lo.z) {
401  ParallelFor(makeSlab(bx3,2,dom_lo.z),
402  [=] AMREX_GPU_DEVICE (int i, int j, int k)
403  {
404  phi_arr(i,j,k-1) = -phi_arr(i,j,k); // ODD BC
405  });
406  } // lo z
407  if (bx.bigEnd(2) >= dom_hi.z) {
408  ParallelFor(makeSlab(bx3,2,dom_hi.z),
409  [=] AMREX_GPU_DEVICE (int i, int j, int k)
410  {
411  phi_arr(i,j,k+1) = phi_arr(i,j,k); // even BC
412  });
413  } // hi z
414  } // mfi
415 
416  // ****************************************************************************
417  // Compute grad(phi) to get distances
418  // ****************************************************************************
419  auto const& phi_arr = phi[0].const_arrays();
420  //auto rhs_arr = rhs[0].arrays();
421  auto dist_arr = walldist[lev]->arrays();
422 
423  ParallelFor(*walldist[lev], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
424  Real dpdx{0}, dpdy{0}, dpdz{0};
425 
426  dpdx = terrpoisson_flux_x(i, j, k, phi_arr[b], zphys_arr[b], dxinv[0]);
427  dpdy = terrpoisson_flux_y(i, j, k, phi_arr[b], zphys_arr[b], dxinv[1]);
428  if (k == dom_lo.z) {
429  dpdz = terrpoisson_flux_zlo_dir(i, j, k, phi_arr[b], zphys_arr[b], dxinv[0], dxinv[1]);
430  } else {
431  // This returns 0 at the wall, hence the need for the separate calc above
432  dpdz = terrpoisson_flux_z(i, j, k, phi_arr[b], zphys_arr[b], dxinv[0], dxinv[1]);
433  }
434 
435  Real magsqr_dphi = dpdx*dpdx + dpdy*dpdy + dpdz*dpdz;
436  Real mag_dphi = std::sqrt(magsqr_dphi);
437 #if 1
438  // Tucker 2003 Eqn 2
439  dist_arr[b](i, j, k) = -mag_dphi + std::sqrt(magsqr_dphi + 2*phi_arr[b](i, j, k));
440 #else
441  // DEBUG: output phi instead
442  if (i==0 && j==0) AllPrint() << "walldist"<<IntVect(i,j,k) << " = " << dist_arr[b](i,j,k) << std::endl;
443  dist_arr[b](i, j, k) = phi_arr[b](i, j, k);
444 #endif
445  // Update RHS source term to explicitly include cross-terms
446  if (n_corr > 0) {
447  // d/dxi ( h_xi * dphi/dzeta )
448  Real phi_zeta_xlo = fourth * dxinv[2] * ( phi_arr[b](i , j, k+1) - phi_arr[b](i , j, k-1)
449  + phi_arr[b](i-1, j, k+1) - phi_arr[b](i-1, j, k-1) );
450  Real phi_zeta_xhi = fourth * dxinv[2] * ( phi_arr[b](i , j, k+1) - phi_arr[b](i , j, k-1)
451  + phi_arr[b](i+1, j, k+1) - phi_arr[b](i+1, j, k-1) );
452  Real h_xi_xlo = Compute_h_xi_AtIface(i , j, k, dxinv, zphys_arr[b]);
453  Real h_xi_xhi = Compute_h_xi_AtIface(i+1, j, k, dxinv, zphys_arr[b]);
454 
455  // d/deta ( h_eta * dphi/dzeta )
456  Real phi_zeta_ylo = fourth * dxinv[2] * ( phi_arr[b](i, j , k+1) - phi_arr[b](i, j , k-1)
457  + phi_arr[b](i, j-1, k+1) - phi_arr[b](i, j-1, k-1) );
458  Real phi_zeta_yhi = fourth * dxinv[2] * ( phi_arr[b](i, j , k+1) - phi_arr[b](i, j , k-1)
459  + phi_arr[b](i, j+1, k+1) - phi_arr[b](i, j+1, k-1) );
460  Real h_eta_ylo = Compute_h_eta_AtJface(i, j , k, dxinv, zphys_arr[b]);
461  Real h_eta_yhi = Compute_h_eta_AtJface(i, j+1, k, dxinv, zphys_arr[b]);
462 
463  // d/dzeta ( h_xi * dphi/dxi )
464  Real phi_xi_zlo = fourth * dxinv[0] * ( phi_arr[b](i+1, j, k ) - phi_arr[b](i-1, j, k )
465  + phi_arr[b](i+1, j, k-1) - phi_arr[b](i-1, j, k-1) );
466  Real phi_xi_zhi = fourth * dxinv[0] * ( phi_arr[b](i+1, j, k ) - phi_arr[b](i-1, j, k )
467  + phi_arr[b](i+1, j, k+1) - phi_arr[b](i-1, j, k+1) );
468  Real h_xi_zlo = Compute_h_xi_AtKface(i, j, k , dxinv, zphys_arr[b]);
469  Real h_xi_zhi = Compute_h_xi_AtKface(i, j, k+1, dxinv, zphys_arr[b]);
470 
471  // d/dzeta ( h_eta * dphi/deta )
472  Real phi_eta_zlo = fourth * dxinv[1] * ( phi_arr[b](i, j+1, k ) - phi_arr[b](i, j-1, k )
473  + phi_arr[b](i, j+1, k-1) - phi_arr[b](i, j-1, k-1) );
474  Real phi_eta_zhi = fourth * dxinv[1] * ( phi_arr[b](i, j+1, k ) - phi_arr[b](i, j-1, k )
475  + phi_arr[b](i, j+1, k+1) - phi_arr[b](i, j-1, k+1) );
476  Real h_eta_zlo = Compute_h_eta_AtKface(i, j, k , dxinv, zphys_arr[b]);
477  Real h_eta_zhi = Compute_h_eta_AtKface(i, j, k+1, dxinv, zphys_arr[b]);
478 
479  Real detJ = Compute_h_zeta_AtCellCenter(i, j, k, dxinv, zphys_arr[b]);
480 
481  rhs_arr[b](i, j, k) = -detJ
482  + dxinv[0] * ( h_xi_xhi * phi_zeta_xhi - h_xi_xlo * phi_zeta_xlo)
483  + dxinv[1] * ( h_eta_yhi * phi_zeta_yhi - h_eta_ylo * phi_zeta_ylo)
484  + dxinv[2] * ( h_xi_zhi * phi_xi_zhi - h_xi_zlo * phi_xi_zlo
485  + h_eta_zhi * phi_eta_zhi - h_eta_zlo * phi_eta_zlo);
486  }
487  });
488  } // corrector loop
489 }
amrex::Real beta
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:10
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtIface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:117
AMREX_FORCE_INLINE AMREX_GPU_DEVICE amrex::Real Compute_h_zeta_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:55
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:184
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtIface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:104
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:198
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtJface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:144
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtJface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:170
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:211
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T terrpoisson_flux_x(int i, int j, int k, amrex::Array4< T const > const &sol, amrex::Array4< T const > const &zp, T dxinv) noexcept
Definition: ERF_TerrainPoisson_3D_K.H:9
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T terrpoisson_flux_zlo_dir(int i, int j, int k, amrex::Array4< T const > const &sol, amrex::Array4< T const > const &zp, T dxinv, T dyinv) noexcept
Definition: ERF_TerrainPoisson_3D_K.H:163
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T terrpoisson_flux_z(int i, int j, int k, amrex::Array4< T const > const &sol, amrex::Array4< T const > const &zp, T dxinv, T dyinv) noexcept
Definition: ERF_TerrainPoisson_3D_K.H:84
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T terrpoisson_flux_y(int i, int j, int k, amrex::Array4< T const > const &sol, amrex::Array4< T const > const &zp, T dyinv) noexcept
Definition: ERF_TerrainPoisson_3D_K.H:47
static int mg_verbose
Definition: ERF.H:1306
amrex::Real poisson_reltol
Definition: ERF_DataStruct.H:1261
int ncorr
Definition: ERF_DataStruct.H:1255
amrex::Real poisson_abstol
Definition: ERF_DataStruct.H:1260
Here is the call graph for this function:

◆ post_timestep()

void ERF::post_timestep ( int  nstep,
double  time,
amrex::Real  dt_lev 
)
314 {
315  BL_PROFILE("ERF::post_timestep()");
316 
317 #ifdef ERF_USE_PARTICLES
318  particleData.Redistribute(z_phys_nd);
319 #endif
320 
321  if (solverChoice.coupling_type == CouplingType::TwoWay)
322  {
323  int ncomp = vars_new[0][Vars::cons].nComp();
324  for (int lev = finest_level-1; lev >= 0; lev--)
325  {
326  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
327  // m is the map scale factor at cell centers
328  // Here we pre-divide (rho S) by m^2 before refluxing
329  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
330  const Box& bx = mfi.tilebox();
331  const Array4< Real> cons_arr = vars_new[lev][Vars::cons].array(mfi);
332  const Array4<const Real> mfx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
333  const Array4<const Real> mfy_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
334  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
335  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
336  {
337  cons_arr(i,j,k,n) /= (mfx_arr(i,j,0)*mfy_arr(i,j,0));
338  });
339  } else {
340  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
341  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
342  {
343  cons_arr(i,j,k,n) *= detJ_arr(i,j,k) / (mfx_arr(i,j,0)*mfy_arr(i,j,0));
344  });
345  }
346  } // mfi
347 
348  // This call refluxes all "slow" cell-centered variables
349  // (i.e. not density or (rho theta) or velocities) from the lev/lev+1 interface onto lev
350  getAdvFluxReg(lev+1)->Reflux(vars_new[lev][Vars::cons], 2, 2, ncomp-2);
351 
352  // Here we multiply (rho S) by m^2 after refluxing
353  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
354  const Box& bx = mfi.tilebox();
355  const Array4< Real> cons_arr = vars_new[lev][Vars::cons].array(mfi);
356  const Array4<const Real> mfx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
357  const Array4<const Real> mfy_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
358  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
359  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
360  {
361  cons_arr(i,j,k,n) *= (mfx_arr(i,j,0)*mfy_arr(i,j,0));
362  });
363  } else {
364  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
365  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
366  {
367  cons_arr(i,j,k,n) *= (mfx_arr(i,j,0)*mfy_arr(i,j,0)) / detJ_arr(i,j,k);
368  });
369  }
370  } // mfi
371 
372  // We need to do this before anything else because refluxing changes the
373  // values of coarse cells underneath fine grids with the assumption they'll
374  // be over-written by averaging down
375  int src_comp;
376  if (solverChoice.anelastic[lev]) {
377  src_comp = 1;
378  } else {
379  src_comp = 0;
380  }
381  int num_comp = ncomp - src_comp;
382  AverageDownTo(lev,src_comp,num_comp);
383  }
384  }
385 
386  if (is_it_time_for_action(nstep, time, dt_lev0, sum_interval, sum_per)) {
389  sum_energy_quantities(time);
390  }
391 
393  if (is_it_time_for_action(nstep, time, dt_lev0, pert_interval, -one)) {
394  turbPert.debug(time);
395  }
396  }
397 
398  if (profile_int > 0 && (nstep+1) % profile_int == 0) {
399  if (destag_profiles) {
400  // all variables cell-centered
401  write_1D_profiles(time);
402  } else {
403  // some variables staggered
405  }
406  }
407 
408  if (solverChoice.rad_type != RadiationType::None)
409  {
410  if ( rad_datalog_int > 0 &&
411  (((nstep+1) % rad_datalog_int == 0) || (nstep==0)) ) {
412  if (rad[0]->hasDatalog()) {
413  rad[0]->WriteDataLog(static_cast<Real>(time+start_time));
414  }
415  }
416  }
417 
418  if (output_1d_column) {
419 #ifdef ERF_USE_NETCDF
420  if (is_it_time_for_action(nstep, time, dt_lev0, column_interval, column_per))
421  {
422  int lev_column = 0;
423  for (int lev = finest_level; lev >= 0; lev--)
424  {
425  Real dx_lev = geom[lev].CellSize(0);
426  Real dy_lev = geom[lev].CellSize(1);
427  int i_lev = static_cast<int>(std::floor(column_loc_x / dx_lev));
428  int j_lev = static_cast<int>(std::floor(column_loc_y / dy_lev));
429  if (grids[lev].contains(IntVect(i_lev,j_lev,0))) lev_column = lev;
430  }
431  writeToNCColumnFile(lev_column, column_file_name, column_loc_x, column_loc_y, time);
432  }
433 #else
434  Abort("To output 1D column files ERF must be compiled with NetCDF");
435 #endif
436  }
437 
439  {
442  {
443  bool is_moist = (micro->Get_Qstate_Moist_Size() > 0);
444  m_w2d->write_planes(istep[0], time+start_time, vars_new, is_moist);
445  }
446  }
447 
448  // Write plane/line sampler data
450  line_sampler->get_sample_data(geom, vars_new);
451  line_sampler->write_sample_data(t_new, istep, ref_ratio, geom);
452  }
454  plane_sampler->get_sample_data(geom, vars_new);
455  plane_sampler->write_sample_data(t_new, istep, ref_ratio, geom);
456  }
457 
458  // Moving terrain
459  if ( solverChoice.terrain_type == TerrainType::MovingFittedMesh )
460  {
461  for (int lev = finest_level; lev >= 0; lev--)
462  {
463  // Copy z_phs_nd and detJ_cc at end of timestep
464  MultiFab::Copy(*z_phys_nd[lev], *z_phys_nd_new[lev], 0, 0, 1, z_phys_nd[lev]->nGrowVect());
465  MultiFab::Copy( *detJ_cc[lev], *detJ_cc_new[lev], 0, 0, 1, detJ_cc[lev]->nGrowVect());
466  MultiFab::Copy(base_state[lev],base_state_new[lev],0,0,BaseState::num_comps,base_state[lev].nGrowVect());
467 
468  make_zcc(geom[lev],*z_phys_nd[lev],*z_phys_cc[lev]);
469  }
470  }
471 
472  if ( solverChoice.init_type==InitType::HindCast and
474  (nstep == 0 or (nstep+1)%m_plot3d_int_1 == 0) )
475  {
476  int levc=finest_level;
477 
478  HurricaneEyeTracker(geom[levc],
479  vars_new[levc],
487 
488  MultiFab& U_new = vars_new[levc][Vars::xvel];
489  MultiFab& V_new = vars_new[levc][Vars::yvel];
490  MultiFab& W_new = vars_new[levc][Vars::zvel];
491 
492  MultiFab mf_cc_vel(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(0,0,0));
493  average_face_to_cellcenter(mf_cc_vel,0,{AMREX_D_DECL(&U_new,&V_new,&W_new)},0);
494 
495  HurricaneMaxVelTracker(geom[levc],
496  mf_cc_vel,
497  t_new[0],
500 
502  geom[levc],
503  vars_new[levc][Vars::cons],
504  t_new[0],
507 
508  std::string filename_tracker = MakeVTKFilename_TrackerCircle(nstep);
509  std::string filename_xy = MakeVTKFilename_EyeTracker_xy(nstep);
510  std::string filename_latlon = MakeFilename_EyeTracker_latlon(nstep);
511  std::string filename_maxvel = MakeFilename_EyeTracker_maxvel(nstep);
512  std::string filename_minpressure = MakeFilename_EyeTracker_minpressure(nstep);
513 
514  if (ParallelDescriptor::IOProcessor()) {
515  WriteVTKPolyline(filename_tracker, hurricane_tracker_circle);
517  WriteLinePlot(filename_latlon, hurricane_eye_track_latlon);
518  WriteLinePlot(filename_maxvel, hurricane_maxvel_vs_time);
519  WriteLinePlot(filename_minpressure, hurricane_minpressure_vs_time);
520  }
521  }
522 
523  if ( solverChoice.init_type==InitType::WRFInput and
525  (nstep == 0 or (nstep+1)%m_plot3d_int_1 == 0)) {
527 
528  std::string filename_tracker = MakeVTKFilename_TrackerCircle(nstep);
529  std::string filename_xy = MakeVTKFilename_EyeTracker_xy(nstep);
530  std::string filename_latlon = MakeFilename_EyeTracker_latlon(nstep);
531 
532  if (ParallelDescriptor::IOProcessor()) {
533  WriteVTKPolyline(filename_tracker, hurricane_tracker_circle);
535  WriteLinePlot(filename_latlon, hurricane_eye_track_latlon);
536  }
537  }
538 
539 } // post_timestep
AMREX_FORCE_INLINE void HurricaneMaxVelTracker(const amrex::Geometry &geom, const amrex::MultiFab &mf_cc_vel, const amrex::Real &time, const amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_eye_track_xy, amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_maxvel_vs_time)
Definition: ERF_HurricaneDiagnostics.H:291
AMREX_FORCE_INLINE void HurricaneEyeTracker(const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, MoistureType moisture_type, const amrex::Vector< amrex::MultiFab > *forecast_state_at_lev, const amrex::Real &hurricane_eye_latitude, const amrex::Real &hurricane_eye_longitude, amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_eye_track_xy, amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_eye_track_latlon, amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_tracker_circle)
Definition: ERF_HurricaneDiagnostics.H:264
AMREX_FORCE_INLINE void HurricaneMinPressureTracker(MoistureType moisture_type, const amrex::Geometry &geom, const amrex::MultiFab &mf_cons_var, const amrex::Real &time, const amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_eye_track_xy, amrex::Vector< std::array< amrex::Real, 2 >> &hurricane_minpressure_vs_time)
Definition: ERF_HurricaneDiagnostics.H:349
void make_zcc(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc)
Definition: ERF_TerrainMetrics.cpp:628
std::string MakeFilename_EyeTracker_maxvel(int nstep)
Definition: ERF_TrackerOutput.cpp:66
static amrex::Real column_loc_y
Definition: ERF.H:1377
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_maxvel_vs_time
Definition: ERF.H:171
static std::string column_file_name
Definition: ERF.H:1378
AMREX_FORCE_INLINE amrex::YAFluxRegister * getAdvFluxReg(int lev)
Definition: ERF.H:1518
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_minpressure_vs_time
Definition: ERF.H:172
static amrex::Real bndry_output_planes_per
Definition: ERF.H:1383
static amrex::Real column_per
Definition: ERF.H:1375
static amrex::Real column_loc_x
Definition: ERF.H:1376
std::string MakeVTKFilename_TrackerCircle(int nstep)
Definition: ERF_TrackerOutput.cpp:24
std::string MakeVTKFilename_EyeTracker_xy(int nstep)
Definition: ERF_TrackerOutput.cpp:38
static int bndry_output_planes_interval
Definition: ERF.H:1382
void HurricaneEyeTracker_WRF(const SolverChoice &solverChoice)
Definition: ERF_HurricaneDiagnostics_WRF.cpp:368
std::string MakeFilename_EyeTracker_minpressure(int nstep)
Definition: ERF_TrackerOutput.cpp:80
void WriteLinePlot(const std::string &filename, amrex::Vector< std::array< amrex::Real, 2 >> &points_xy)
Definition: ERF_Write1DProfiles.cpp:593
static int output_1d_column
Definition: ERF.H:1373
void WriteVTKPolyline(const std::string &filename, amrex::Vector< std::array< amrex::Real, 2 >> &points_xy)
Definition: ERF_TrackerOutput.cpp:94
std::string MakeFilename_EyeTracker_latlon(int nstep)
Definition: ERF_TrackerOutput.cpp:52
static int column_interval
Definition: ERF.H:1374
amrex::Real hurricane_eye_latitude
Definition: ERF_DataStruct.H:1444
amrex::Real hurricane_eye_longitude
Definition: ERF_DataStruct.H:1444
bool io_hurricane_eye_tracker
Definition: ERF_DataStruct.H:1443

Referenced by EvolveOneStep().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ post_update()

void ERF::post_update ( amrex::MultiFab &  state_mf,
amrex::Real  time,
const amrex::Geometry &  geom 
)
private

◆ print_banner()

void ERF::print_banner ( MPI_Comm  comm,
std::ostream &  out 
)
static
65  : " << msg << std::endl;
66 }
67 
68 void ERF::print_banner (MPI_Comm comm, std::ostream& out)
69 {
70 #ifdef AMREX_USE_MPI
71  int irank = 0;
72  int num_ranks = 1;
73  MPI_Comm_size(comm, &num_ranks);
74  MPI_Comm_rank(comm, &irank);
75 
76  // Only root process does the printing
77  if (irank != 0) return;
78 #else
79  amrex::ignore_unused(comm);
80 #endif
81 
82  auto etime = std::chrono::system_clock::now();
83  auto etimet = std::chrono::system_clock::to_time_t(etime);
84 #ifndef _WIN32
85  char time_buf[64];
86  ctime_r(&etimet, time_buf);
87  const std::string tstamp(time_buf);
88 #else
89  char* time_buf = new char[64];
90  ctime_s(time_buf, 64, &etimet);
91  const std::string tstamp(time_buf);
92 #endif
93 
94  const char* githash1 = amrex::buildInfoGetGitHash(1);
95  const char* githash2 = amrex::buildInfoGetGitHash(2);
96 
97  // clang-format off
98  out << dbl_line
99  << " ERF (https://github.com/erf-model/ERF)"
100  << std::endl << std::endl
101  << " ERF Git SHA :: " << githash1 << std::endl
102  << " AMReX Git SHA :: " << githash2 << std::endl
103  << " AMReX version :: " << amrex::Version() << std::endl << std::endl
104  << " Exec. time :: " << tstamp
105  << " Build time :: " << amrex::buildInfoGetBuildDate() << std::endl
106  << " C++ compiler :: " << amrex::buildInfoGetComp()
107  << " " << amrex::buildInfoGetCompVersion() << std::endl << std::endl
108  << " MPI :: "
109 #ifdef AMREX_USE_MPI
110  << "ON (Num. ranks = " << num_ranks << ")" << std::endl
111 #else
112  << "OFF " << std::endl
113 #endif
114  << " GPU :: "
115 #ifdef AMREX_USE_GPU
116  << "ON "
117 #if defined(AMREX_USE_CUDA)
118  << "(Backend: CUDA)"
119 #elif defined(AMREX_USE_HIP)
120  << "(Backend: HIP)"
121 #elif defined(AMREX_USE_SYCL)
122  << "(Backend: SYCL)"
123 #endif
124  << std::endl
125 #else
126  << "OFF" << std::endl
127 #endif
128  << " OpenMP :: "
129 #ifdef AMREX_USE_OMP
130  << "ON (Num. threads = " << omp_get_max_threads() << ")" << std::endl
131 #else
132  << "OFF" << std::endl
133 #endif
134  << std::endl;
135 
ERF()
Definition: ERF_Constructors.cpp:21
const char * buildInfoGetBuildDate()
const char * buildInfoGetComp()
const char * buildInfoGetCompVersion()

Referenced by main().

Here is the caller graph for this function:

◆ print_error()

void ERF::print_error ( MPI_Comm  comm,
const std::string &  msg 
)
static
43  :
44  input_file : Input file with simulation settings
45 
46 Optional:
47  param=value : Overrides for parameters during runtime
48 )doc" << std::endl;
49 }
50 
51 void ERF::print_error (MPI_Comm comm, const std::string& msg)
52 {
53 #ifdef AMREX_USE_MPI
54  int irank = 0;
55  int num_ranks = 1;
56  MPI_Comm_size(comm, &num_ranks);
57  MPI_Comm_rank(comm, &irank);
58 

Referenced by main().

Here is the caller graph for this function:

◆ print_summary()

static void ERF::print_summary ( std::ostream &  )
static

◆ print_tpls()

void ERF::print_tpls ( std::ostream &  out)
static
140  ://github.com/erf-model/ERF/blob/development/LICENSE for details. "
141  << dash_line << std::endl;
142  // clang-format on
143 }
144 
145 void ERF::print_tpls (std::ostream& out)
146 {
147  amrex::Vector<std::string> tpls;
148 
149 #ifdef ERF_USE_NETCDF
150  tpls.push_back(std::string("NetCDF ") + NC_VERSION);
151 #endif
152 #ifdef AMREX_USE_SUNDIALS
153  tpls.push_back(std::string("SUNDIALS ") + SUNDIALS_VERSION);
154 #endif
155 
156  if (!tpls.empty()) {
157  out << " Enabled third-party libraries: ";
158  for (const auto& val : tpls) {
static void print_tpls(std::ostream &)
Definition: ERF_ConsoleIO.cpp:137

◆ print_usage()

void ERF::print_usage ( MPI_Comm  comm,
std::ostream &  out 
)
static
27 {
28 #ifdef AMREX_USE_MPI
29  int irank = 0;
30  int num_ranks = 1;
31  MPI_Comm_size(comm, &num_ranks);
32  MPI_Comm_rank(comm, &irank);
33 
34  // Only root process does the printing
35  if (irank != 0) return;
36 #else
37  amrex::ignore_unused(comm);
38 #endif
39 
40  out << R"doc(Usage:
41  ERF3d.*.ex <input_file> [param=value] [param=value] ...

Referenced by main().

Here is the caller graph for this function:

◆ project_initial_velocity()

void ERF::project_initial_velocity ( int  lev,
amrex::Real  time,
amrex::Real  dt 
)

Project the single-level velocity field to enforce the anelastic constraint Note that the level may or may not be level zero

32 {
33  BL_PROFILE("ERF::project_initial_velocity()");
34  // Impose FillBoundary on density since we use it in the conversion of velocity to momentum
35  vars_new[lev][Vars::cons].FillBoundary(geom[lev].periodicity());
36 
37  const MultiFab* c_vfrac = nullptr;
38  if (solverChoice.terrain_type == TerrainType::EB) {
39  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
40  }
41 
42  VelocityToMomentum(vars_new[lev][Vars::xvel], IntVect{0},
43  vars_new[lev][Vars::yvel], IntVect{0},
44  vars_new[lev][Vars::zvel], IntVect{0},
45  vars_new[lev][Vars::cons],
46  rU_new[lev], rV_new[lev], rW_new[lev],
47  Geom(lev).Domain(), domain_bcs_type, c_vfrac);
48 
49  Vector<MultiFab> tmp_mom;
50 
51  tmp_mom.push_back(MultiFab(vars_new[lev][Vars::cons],make_alias,0,1));
52  tmp_mom.push_back(MultiFab(rU_new[lev],make_alias,0,1));
53  tmp_mom.push_back(MultiFab(rV_new[lev],make_alias,0,1));
54  tmp_mom.push_back(MultiFab(rW_new[lev],make_alias,0,1));
55 
56  // If at lev > 0 we must first fill the velocities at the c/f interface -- this must
57  // be done *after* the projection at lev-1
58  if (lev > 0) {
59  int levc = lev-1;
60 
61  const MultiFab* c_vfrac_crse = nullptr;
62  if (solverChoice.terrain_type == TerrainType::EB) {
63  c_vfrac_crse = &((get_eb(levc).get_const_factory())->getVolFrac());
64  }
65 
66  MultiFab& S_new_crse = vars_new[levc][Vars::cons];
67  MultiFab& U_new_crse = vars_new[levc][Vars::xvel];
68  MultiFab& V_new_crse = vars_new[levc][Vars::yvel];
69  MultiFab& W_new_crse = vars_new[levc][Vars::zvel];
70 
71  VelocityToMomentum(U_new_crse, IntVect{0}, V_new_crse, IntVect{0}, W_new_crse, IntVect{0}, S_new_crse,
72  rU_new[levc], rV_new[levc], rW_new[levc],
73  Geom(levc).Domain(), domain_bcs_type, c_vfrac_crse);
74 
75  rU_new[levc].FillBoundary(geom[levc].periodicity());
76  FPr_u[levc].RegisterCoarseData({&rU_new[levc], &rU_new[levc]}, {time, time+l_dt});
77 
78  rV_new[levc].FillBoundary(geom[levc].periodicity());
79  FPr_v[levc].RegisterCoarseData({&rV_new[levc], &rV_new[levc]}, {time, time+l_dt});
80 
81  rW_new[levc].FillBoundary(geom[levc].periodicity());
82  FPr_w[levc].RegisterCoarseData({&rW_new[levc], &rW_new[levc]}, {time, time+l_dt});
83  }
84 
85  // Use the same time that was registered in the FillPatcher above so that the
86  // FillSet assertion (time >= crse_times[0] && time <= crse_times[1]) is satisfied
87  // when called at non-zero simulation time (restart or mid-run regrid).
88  project_momenta(lev, time, l_dt, tmp_mom);
89 
91  vars_new[lev][Vars::yvel],
92  vars_new[lev][Vars::zvel],
93  vars_new[lev][Vars::cons],
94  rU_new[lev], rV_new[lev], rW_new[lev],
95  Geom(lev).Domain(), domain_bcs_type, c_vfrac);
96  }
void project_momenta(int lev, amrex::Real l_time, amrex::Real l_dt, amrex::Vector< amrex::MultiFab > &vars)
Definition: ERF_PoissonSolve.cpp:102
Here is the call graph for this function:

◆ project_momenta()

void ERF::project_momenta ( int  lev,
amrex::Real  l_time,
amrex::Real  l_dt,
amrex::Vector< amrex::MultiFab > &  vars 
)

Project the single-level momenta to enforce the anelastic constraint Note that the level may or may not be level zero

103 {
104  BL_PROFILE("ERF::project_momenta()");
105  //
106  // If at lev > 0 we must first fill the momenta at the c/f interface with interpolated coarse values
107  //
108  if (lev > 0) {
109  PhysBCFunctNoOp null_bc;
110  FPr_u[lev-1].FillSet(mom_mf[IntVars::xmom], l_time, null_bc, domain_bcs_type);
111  FPr_v[lev-1].FillSet(mom_mf[IntVars::ymom], l_time, null_bc, domain_bcs_type);
112  FPr_w[lev-1].FillSet(mom_mf[IntVars::zmom], l_time, null_bc, domain_bcs_type);
113  }
114 
115  // Make sure the solver only sees the levels over which we are solving
116  Vector<BoxArray> ba_tmp; ba_tmp.push_back(mom_mf[Vars::cons].boxArray());
117  Vector<DistributionMapping> dm_tmp; dm_tmp.push_back(mom_mf[Vars::cons].DistributionMap());
118  Vector<Geometry> geom_tmp; geom_tmp.push_back(geom[lev]);
119 
120  Box domain = geom[lev].Domain();
121 
122  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp, 1);
123 
124  Vector<MultiFab> rhs;
125  Vector<MultiFab> phi;
126 
127  if (solverChoice.terrain_type == TerrainType::EB)
128  {
129  rhs.resize(1); rhs[0].define(ba_tmp[0], dm_tmp[0], 1, 0, MFInfo(), EBFactory(lev));
130  phi.resize(1); phi[0].define(ba_tmp[0], dm_tmp[0], 1, 1, MFInfo(), EBFactory(lev));
131  } else {
132  rhs.resize(1); rhs[0].define(ba_tmp[0], dm_tmp[0], 1, 0);
133  phi.resize(1); phi[0].define(ba_tmp[0], dm_tmp[0], 1, 1);
134  }
135 
136  MultiFab rhs_lev(rhs[0], make_alias, 0, 1);
137  MultiFab phi_lev(phi[0], make_alias, 0, 1);
138 
139  auto dx = geom[lev].CellSizeArray();
140  auto dxInv = geom[lev].InvCellSizeArray();
141 
142  // Inflow on an x-face -- note only the normal velocity is used in the projection
143  if (domain_bc_type[0] == "Inflow" || domain_bc_type[3] == "Inflow") {
145  IntVect{1,0,0},t_new[lev],BCVars::xvel_bc,false);
146  }
147 
148  // Inflow on a y-face -- note only the normal velocity is used in the projection
149  if (domain_bc_type[1] == "Inflow" || domain_bc_type[4] == "Inflow") {
151  IntVect{0,1,0},t_new[lev],BCVars::yvel_bc,false);
152  }
153 
154  if (domain_bc_type[0] == "Inflow" || domain_bc_type[3] == "Inflow" ||
155  domain_bc_type[1] == "Inflow" || domain_bc_type[4] == "Inflow") {
156 
157  const MultiFab* c_vfrac = nullptr;
158  if (solverChoice.terrain_type == TerrainType::EB) {
159  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
160  }
161 
162  VelocityToMomentum(vars_new[lev][Vars::xvel], IntVect{0},
163  vars_new[lev][Vars::yvel], IntVect{0},
164  vars_new[lev][Vars::zvel], IntVect{0},
165  vars_new[lev][Vars::cons],
166  mom_mf[IntVars::xmom],
167  mom_mf[IntVars::ymom],
168  mom_mf[IntVars::zmom],
169  Geom(lev).Domain(),
170  domain_bcs_type, c_vfrac);
171  }
172 
173  // If !fixed_density, we must convert (rho u) which came in
174  // to (rho0 u) which is what we will project
175  if (!solverChoice.fixed_density[lev]) {
176  ConvertForProjection(mom_mf[Vars::cons], r_hse,
177  mom_mf[IntVars::xmom],
178  mom_mf[IntVars::ymom],
179  mom_mf[IntVars::zmom],
180  Geom(lev).Domain(),
182  }
183 
184  //
185  // ****************************************************************************
186  // Now convert the rho0w MultiFab to hold Omega rather than rhow
187  // ****************************************************************************
188  //
189  if (solverChoice.mesh_type == MeshType::VariableDz)
190  {
191  for ( MFIter mfi(rhs_lev,TilingIfNotGPU()); mfi.isValid(); ++mfi)
192  {
193  const Array4<Real const>& rho0u_arr = mom_mf[IntVars::xmom].const_array(mfi);
194  const Array4<Real const>& rho0v_arr = mom_mf[IntVars::ymom].const_array(mfi);
195  const Array4<Real >& rho0w_arr = mom_mf[IntVars::zmom].array(mfi);
196 
197  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
198  const Array4<Real const>& mf_u = mapfac[lev][MapFacType::u_x]->const_array(mfi);
199  const Array4<Real const>& mf_v = mapfac[lev][MapFacType::v_y]->const_array(mfi);
200 
201  //
202  // Define Omega from (rho0 W) but store it in the same array
203  //
204  Box tbz = mfi.nodaltilebox(2);
205  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
206  if (k == 0) {
207  rho0w_arr(i,j,k) = zero;
208  } else {
209  Real rho0w = rho0w_arr(i,j,k);
210  rho0w_arr(i,j,k) = OmegaFromW(i,j,k,rho0w,
211  rho0u_arr,rho0v_arr,
212  mf_u,mf_v,z_nd,dxInv);
213  }
214  });
215  } // mfi
216  }
217 
218  // ****************************************************************************
219  // Allocate fluxes
220  // ****************************************************************************
221  Vector<Array<MultiFab,AMREX_SPACEDIM> > fluxes;
222  fluxes.resize(1);
223  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
224  if (solverChoice.terrain_type == TerrainType::EB) {
225  fluxes[0][idim].define(convert(ba_tmp[0], IntVect::TheDimensionVector(idim)), dm_tmp[0], 1, 0, MFInfo(), EBFactory(lev));
226  } else {
227  fluxes[0][idim].define(convert(ba_tmp[0], IntVect::TheDimensionVector(idim)), dm_tmp[0], 1, 0);
228  }
229  }
230 
231  // ****************************************************************************
232  // Initialize phi to 0
233  // (It is essential that we do this in order to fill the corners; these are never
234  // used but the Saxpy requires the values to be initialized.)
235  // ****************************************************************************
236  phi_lev.setVal(0.0);
237 
238  // ****************************************************************************
239  // Break into subdomains
240  // ****************************************************************************
241 
242  std::map<int,int> index_map;
243 
244  BoxArray ba(grids[lev]);
245 
246  Vector<MultiFab> rhs_sub; rhs_sub.resize(1);
247  Vector<MultiFab> phi_sub; phi_sub.resize(1);
248  Vector<Array<MultiFab,AMREX_SPACEDIM>> fluxes_sub; fluxes_sub.resize(1);
249 
250  MultiFab ax_sub, ay_sub, az_sub, dJ_sub, znd_sub;
251  MultiFab mfmx_sub, mfmy_sub;
252 
253  Array<MultiFab,AMREX_SPACEDIM> rho0_u_sub;
254  Array<MultiFab const*, AMREX_SPACEDIM> rho0_u_const;
255 
256  // If we are going to solve with MLMG then we do not need to break this into subdomains
257  bool will_solve_with_mlmg = false;
258  if (solverChoice.mesh_type == MeshType::ConstantDz) {
259  will_solve_with_mlmg = true;
260 #ifdef ERF_USE_FFT
261  if (use_fft) {
262  bool all_boxes_ok = true;
263  for (int isub = 0; isub < subdomains[lev].size(); ++isub) {
264  Box my_region(subdomains[lev][isub].minimalBox());
265  bool boxes_make_rectangle = (my_region.numPts() == subdomains[lev][isub].numPts());
266  if (!boxes_make_rectangle) {
267  all_boxes_ok = false;
268  }
269  } // isub
270  if (all_boxes_ok) {
271  will_solve_with_mlmg = false;
272  }
273  } // use_fft
274 #else
275  if (use_fft) {
276  amrex::Warning("You set use_fft=true but didn't build with USE_FFT = TRUE; defaulting to MLMG");
277  }
278 #endif
279  } // No terrain or grid stretching
280 
281  for (int isub = 0; isub < subdomains[lev].size(); ++isub)
282  {
283  BoxList bl_sub;
284  Vector<int> dm_sub;
285 
286  for (int j = 0; j < ba.size(); j++)
287  {
288  if (subdomains[lev][isub].intersects(ba[j]))
289  {
290  //
291  // Note that bl_sub.size() is effectively a counter which is
292  // incremented above
293  //
294  // if (ParallelDescriptor::MyProc() == j) {
295  // }
296  index_map[bl_sub.size()] = j;
297 
298  bl_sub.push_back(grids[lev][j]);
299  dm_sub.push_back(dmap[lev][j]);
300  } // intersects
301  } // loop over ba (j)
302 
303  BoxArray ba_sub(bl_sub);
304 
305  BoxList bl2d_sub = ba_sub.boxList();
306  for (auto& b : bl2d_sub) {
307  b.setRange(2,0);
308  }
309  BoxArray ba2d_sub(std::move(bl2d_sub));
310 
311  // Define MultiFabs that hold only the data in this particular subdomain
312  if (solverChoice.terrain_type == TerrainType::EB) {
313  if (ba_sub != ba) {
314  amrex::Print() << "EB Solves with multiple regions is not yet supported" << std::endl;
315  }
316  rhs_sub[0].define(ba_sub, DistributionMapping(dm_sub), 1, rhs_lev.nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
317  phi_sub[0].define(ba_sub, DistributionMapping(dm_sub), 1, phi_lev.nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
318 
319  mfmx_sub.define(ba2d_sub, DistributionMapping(dm_sub), 1, mapfac[lev][MapFacType::m_x]->nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
320  mfmy_sub.define(ba2d_sub, DistributionMapping(dm_sub), 1, mapfac[lev][MapFacType::m_y]->nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
321  dJ_sub.define(ba_sub, DistributionMapping(dm_sub), 1, detJ_cc[lev]->nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
322 
323  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
324  fluxes_sub[0][idim].define(convert(ba_sub, IntVect::TheDimensionVector(idim)), DistributionMapping(dm_sub), 1,
325  IntVect::TheZeroVector(), MFInfo{}.SetAlloc(false), EBFactory(lev));
326  }
327  rho0_u_sub[0].define(convert(ba_sub, IntVect::TheDimensionVector(0)), DistributionMapping(dm_sub), 1,
328  mom_mf[IntVars::xmom].nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
329  rho0_u_sub[1].define(convert(ba_sub, IntVect::TheDimensionVector(1)), DistributionMapping(dm_sub), 1,
330  mom_mf[IntVars::ymom].nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
331  rho0_u_sub[2].define(convert(ba_sub, IntVect::TheDimensionVector(2)), DistributionMapping(dm_sub), 1,
332  mom_mf[IntVars::zmom].nGrowVect(), MFInfo{}.SetAlloc(false), EBFactory(lev));
333  } else {
334  rhs_sub[0].define(ba_sub, DistributionMapping(dm_sub), 1, rhs_lev.nGrowVect(), MFInfo{}.SetAlloc(false));
335  phi_sub[0].define(ba_sub, DistributionMapping(dm_sub), 1, phi_lev.nGrowVect(), MFInfo{}.SetAlloc(false));
336 
337  mfmx_sub.define(ba2d_sub, DistributionMapping(dm_sub), 1, mapfac[lev][MapFacType::m_x]->nGrowVect(), MFInfo{}.SetAlloc(false));
338  mfmy_sub.define(ba2d_sub, DistributionMapping(dm_sub), 1, mapfac[lev][MapFacType::m_y]->nGrowVect(), MFInfo{}.SetAlloc(false));
339  dJ_sub.define(ba_sub, DistributionMapping(dm_sub), 1, detJ_cc[lev]->nGrowVect(), MFInfo{}.SetAlloc(false));
340 
341  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
342  fluxes_sub[0][idim].define(convert(ba_sub, IntVect::TheDimensionVector(idim)), DistributionMapping(dm_sub), 1,
343  IntVect::TheZeroVector(), MFInfo{}.SetAlloc(false));
344  }
345  rho0_u_sub[0].define(convert(ba_sub, IntVect::TheDimensionVector(0)), DistributionMapping(dm_sub), 1,
346  mom_mf[IntVars::xmom].nGrowVect(), MFInfo{}.SetAlloc(false));
347  rho0_u_sub[1].define(convert(ba_sub, IntVect::TheDimensionVector(1)), DistributionMapping(dm_sub), 1,
348  mom_mf[IntVars::ymom].nGrowVect(), MFInfo{}.SetAlloc(false));
349  rho0_u_sub[2].define(convert(ba_sub, IntVect::TheDimensionVector(2)), DistributionMapping(dm_sub), 1,
350  mom_mf[IntVars::zmom].nGrowVect(), MFInfo{}.SetAlloc(false));
351  }
352 
353  // Link the new MultiFabs to the FABs in the original MultiFabs (no copy required)
354  for (MFIter mfi(rhs_sub[0]); mfi.isValid(); ++mfi)
355  {
356  int orig_index = index_map[mfi.index()];
357  rhs_sub[0].setFab(mfi, FArrayBox(rhs_lev[orig_index], amrex::make_alias, 0, 1));
358  phi_sub[0].setFab(mfi, FArrayBox(phi_lev[orig_index], amrex::make_alias, 0, 1));
359 
360  mfmx_sub.setFab(mfi, FArrayBox((*mapfac[lev][MapFacType::m_x])[orig_index], amrex::make_alias, 0, 1));
361  mfmy_sub.setFab(mfi, FArrayBox((*mapfac[lev][MapFacType::m_y])[orig_index], amrex::make_alias, 0, 1));
362 
363  fluxes_sub[0][0].setFab(mfi,FArrayBox(fluxes[0][0][orig_index], amrex::make_alias, 0, 1));
364  fluxes_sub[0][1].setFab(mfi,FArrayBox(fluxes[0][1][orig_index], amrex::make_alias, 0, 1));
365  fluxes_sub[0][2].setFab(mfi,FArrayBox(fluxes[0][2][orig_index], amrex::make_alias, 0, 1));
366 
367  rho0_u_sub[0].setFab(mfi,FArrayBox(mom_mf[IntVars::xmom][orig_index], amrex::make_alias, 0, 1));
368  rho0_u_sub[1].setFab(mfi,FArrayBox(mom_mf[IntVars::ymom][orig_index], amrex::make_alias, 0, 1));
369  rho0_u_sub[2].setFab(mfi,FArrayBox(mom_mf[IntVars::zmom][orig_index], amrex::make_alias, 0, 1));
370  }
371 
372  rho0_u_const[0] = &rho0_u_sub[0];
373  rho0_u_const[1] = &rho0_u_sub[1];
374  rho0_u_const[2] = &rho0_u_sub[2];
375 
376  if (solverChoice.mesh_type != MeshType::ConstantDz) {
377  ax_sub.define(convert(ba_sub,IntVect(1,0,0)), DistributionMapping(dm_sub), 1,
378  ax[lev]->nGrowVect(), MFInfo{}.SetAlloc(false));
379  ay_sub.define(convert(ba_sub,IntVect(0,1,0)), DistributionMapping(dm_sub), 1,
380  ay[lev]->nGrowVect(), MFInfo{}.SetAlloc(false));
381  az_sub.define(convert(ba_sub,IntVect(0,0,1)), DistributionMapping(dm_sub), 1,
382  az[lev]->nGrowVect(), MFInfo{}.SetAlloc(false));
383  znd_sub.define(convert(ba_sub,IntVect(1,1,1)), DistributionMapping(dm_sub), 1,
384  z_phys_nd[lev]->nGrowVect(), MFInfo{}.SetAlloc(false));
385 
386  for (MFIter mfi(rhs_sub[0]); mfi.isValid(); ++mfi) {
387  int orig_index = index_map[mfi.index()];
388  ax_sub.setFab(mfi, FArrayBox((*ax[lev])[orig_index], amrex::make_alias, 0, 1));
389  ay_sub.setFab(mfi, FArrayBox((*ay[lev])[orig_index], amrex::make_alias, 0, 1));
390  az_sub.setFab(mfi, FArrayBox((*az[lev])[orig_index], amrex::make_alias, 0, 1));
391  znd_sub.setFab(mfi, FArrayBox((*z_phys_nd[lev])[orig_index], amrex::make_alias, 0, 1));
392  dJ_sub.setFab(mfi, FArrayBox((*detJ_cc[lev])[orig_index], amrex::make_alias, 0, 1));
393  }
394  }
395 
396  if (solverChoice.terrain_type == TerrainType::EB) {
397  for (MFIter mfi(rhs_sub[0]); mfi.isValid(); ++mfi) {
398  int orig_index = index_map[mfi.index()];
399  dJ_sub.setFab(mfi, FArrayBox((*detJ_cc[lev])[orig_index], amrex::make_alias, 0, 1));
400  }
401  }
402 
403  // ****************************************************************************
404  // Compute divergence which will form RHS
405  // Note that we replace "rho0w" with the contravariant momentum, Omega
406  // ****************************************************************************
407 
408  compute_divergence(lev, rhs_sub[0], rho0_u_const, geom_tmp[0]);
409 
410  Real rhsnorm;
411 
412  // Max norm over the entire MultiFab
413  rhsnorm = rhs_sub[0].norm0();
414 
415  if (mg_verbose > 0) {
416  bool local = false;
417  Real sum = volWgtSumMF(lev,rhs_sub[0],0,dJ_sub,mfmx_sub,mfmy_sub,false,local);
418  Print() << "Max/L2 norm of divergence before solve in subdomain " << isub << " at level " << lev << " : " << rhsnorm << " " <<
419  rhs_sub[0].norm2() << " and volume-weighted sum " << sum << std::endl;
420  }
421 
422  if (lev == 0 && solverChoice.use_real_bcs)
423  {
424  // We always use VariableDz if use_real_bcs is true
425  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type == MeshType::VariableDz);
426 
427  // Note that we always impose the projections one level at a time so this will always be a vector of length 1
428  Array<MultiFab*, AMREX_SPACEDIM> rho0_u_vec =
429  {&mom_mf[IntVars::xmom], &mom_mf[IntVars::ymom], &mom_mf[IntVars::zmom]};
430  Array<MultiFab*, AMREX_SPACEDIM> area_vec = {ax[lev].get(), ay[lev].get(), az[lev].get()};
431  //
432  // Modify ax,ay,ax to include the map factors as used in the divergence calculation
433  // We do this here so that it is seen in the call to enforceInOutSolvability
434  //
435  for (MFIter mfi(rhs_lev); mfi.isValid(); ++mfi)
436  {
437  Box xbx = mfi.nodaltilebox(0);
438  Box ybx = mfi.nodaltilebox(1);
439  Box zbx = mfi.nodaltilebox(2);
440  const Array4<Real >& ax_ar = ax[lev]->array(mfi);
441  const Array4<Real >& ay_ar = ay[lev]->array(mfi);
442  const Array4<Real >& az_ar = az[lev]->array(mfi);
443  const Array4<Real const>& mf_uy = mapfac[lev][MapFacType::u_y]->const_array(mfi);
444  const Array4<Real const>& mf_vx = mapfac[lev][MapFacType::v_x]->const_array(mfi);
445  const Array4<Real const>& mf_mx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
446  const Array4<Real const>& mf_my = mapfac[lev][MapFacType::m_y]->const_array(mfi);
448  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
449  {
450  ax_ar(i,j,k) /= mf_uy(i,j,0);
451  },
452  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
453  {
454  ay_ar(i,j,k) /= mf_vx(i,j,0);
455  },
456  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
457  {
458  az_ar(i,j,k) /= (mf_mx(i,j,0)*mf_my(i,j,0));
459  });
460  } // mfi
461 
462  if (mg_verbose > 0) {
463  Print() << "Calling enforceInOutSolvability" << std::endl;
464  }
465  enforceInOutSolvability(lev, rho0_u_vec, area_vec, geom[lev]);
466 
467  //
468  // Return ax,ay,ax to their original definition
469  //
470  for (MFIter mfi(rhs_lev); mfi.isValid(); ++mfi)
471  {
472  Box xbx = mfi.nodaltilebox(0);
473  Box ybx = mfi.nodaltilebox(1);
474  Box zbx = mfi.nodaltilebox(2);
475  const Array4<Real >& ax_ar = ax[lev]->array(mfi);
476  const Array4<Real >& ay_ar = ay[lev]->array(mfi);
477  const Array4<Real >& az_ar = az[lev]->array(mfi);
478  const Array4<Real const>& mf_uy = mapfac[lev][MapFacType::u_y]->const_array(mfi);
479  const Array4<Real const>& mf_vx = mapfac[lev][MapFacType::v_x]->const_array(mfi);
480  const Array4<Real const>& mf_mx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
481  const Array4<Real const>& mf_my = mapfac[lev][MapFacType::m_y]->const_array(mfi);
483  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
484  {
485  ax_ar(i,j,k) *= mf_uy(i,j,0);
486  },
487  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
488  {
489  ay_ar(i,j,k) *= mf_vx(i,j,0);
490  },
491  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
492  {
493  az_ar(i,j,k) *= (mf_mx(i,j,0)*mf_my(i,j,0));
494  });
495  } // mfi
496 
497  compute_divergence(lev, rhs_lev, rho0_u_const, geom_tmp[0]);
498 
499  // Re-define max norm over the entire MultiFab
500  rhsnorm = rhs_lev.norm0();
501 
502  if (mg_verbose > 0)
503  {
504  bool local = false;
505  Real sum = volWgtSumMF(lev,rhs_sub[0],0,dJ_sub,mfmx_sub,mfmy_sub,false,local);
506  Print() << "Max/L2 norm of divergence before solve at level " << lev << " : " << rhsnorm << " " <<
507  rhs_lev.norm2() << " and volume-weighted sum " << sum << std::endl;
508  }
509  } // lev 0 && use_real_bcs
510 
511  // *******************************************************************************************
512  // Enforce solvability if the problem is singular (i.e all sides Neumann or periodic)
513  // Note that solves at lev > 0 are always singular because we impose Neumann bc's on all sides
514  // *******************************************************************************************
515  bool is_singular = true;
516  if (lev == 0) {
517  if ( (domain_bc_type[0] == "Outflow" || domain_bc_type[0] == "Open") && !solverChoice.use_real_bcs ) is_singular = false;
518  if ( (domain_bc_type[1] == "Outflow" || domain_bc_type[1] == "Open") && !solverChoice.use_real_bcs ) is_singular = false;
519  if ( (domain_bc_type[3] == "Outflow" || domain_bc_type[3] == "Open") && !solverChoice.use_real_bcs ) is_singular = false;
520  if ( (domain_bc_type[4] == "Outflow" || domain_bc_type[4] == "Open") && !solverChoice.use_real_bcs ) is_singular = false;
521  if ( (domain_bc_type[5] == "Outflow" || domain_bc_type[5] == "Open") ) is_singular = false;
522  } else {
523  Box my_region(subdomains[lev][isub].minimalBox());
524  if ( (domain_bc_type[5] == "Outflow" || domain_bc_type[5] == "Open") && (my_region.bigEnd(2) == domain.bigEnd(2)) ) is_singular = false;
525  }
526 
527  if (is_singular)
528  {
529  bool local = false;
530  Real sum = volWgtSumMF(lev,rhs_sub[0],0,dJ_sub,mfmx_sub,mfmy_sub,false,local);
531 
532  Real vol;
533  if (solverChoice.mesh_type == MeshType::ConstantDz) {
534  vol = rhs_sub[0].boxArray().numPts();
535  } else {
536  vol = dJ_sub.sum();
537  }
538 
539  sum /= (vol * dx[0] * dx[1] * dx[2]);
540 
541  for (MFIter mfi(rhs_sub[0]); mfi.isValid(); ++mfi)
542  {
543  rhs_sub[0][mfi.index()].template minus<RunOn::Device>(sum);
544  }
545  if (mg_verbose > 0) {
546  amrex::Print() << " Subtracting " << sum << " from rhs in subdomain " << isub << std::endl;
547 
548  sum = volWgtSumMF(lev,rhs_sub[0],0,dJ_sub,mfmx_sub,mfmy_sub,false,local);
549  Print() << "Sum after subtraction " << sum << " in subdomain " << isub << std::endl;
550  }
551 
552  } // if is_singular
553 
554  rhsnorm = rhs_sub[0].norm0();
555 
556  // ****************************************************************************
557  // No need to build the solver if RHS == 0
558  // ****************************************************************************
559  if (rhsnorm <= solverChoice.poisson_abstol) return;
560 
561  Real start_step = static_cast<Real>(ParallelDescriptor::second());
562 
563  if (mg_verbose > 0) {
564  amrex::Print() << " Solving in subdomain " << isub << " of " << subdomains[lev].size() << " bins at level " << lev << std::endl;
565  }
566 
567  if (solverChoice.mesh_type == MeshType::VariableDz) {
568  //
569  // Modify ax,ay,ax to include the map factors as used in the divergence calculation
570  // We do this here to set the coefficients used in the stencil -- the extra factor
571  // of the mapfac comes from the gradient
572  //
573  for (MFIter mfi(rhs_sub[0]); mfi.isValid(); ++mfi)
574  {
575  Box xbx = mfi.nodaltilebox(0);
576  Box ybx = mfi.nodaltilebox(1);
577  Box zbx = mfi.nodaltilebox(2);
578  const Array4<Real >& ax_ar = ax_sub.array(mfi);
579  const Array4<Real >& ay_ar = ay_sub.array(mfi);
580  const Array4<Real >& az_ar = az_sub.array(mfi);
581  const Array4<Real const>& mf_ux = mapfac[lev][MapFacType::u_x]->const_array(mfi);
582  const Array4<Real const>& mf_uy = mapfac[lev][MapFacType::u_y]->const_array(mfi);
583  const Array4<Real const>& mf_vx = mapfac[lev][MapFacType::v_x]->const_array(mfi);
584  const Array4<Real const>& mf_vy = mapfac[lev][MapFacType::v_y]->const_array(mfi);
585  const Array4<Real const>& mf_mx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
586  const Array4<Real const>& mf_my = mapfac[lev][MapFacType::m_y]->const_array(mfi);
588  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
589  {
590  ax_ar(i,j,k) *= (mf_ux(i,j,0) / mf_uy(i,j,0));
591  },
592  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
593  {
594  ay_ar(i,j,k) *= (mf_vy(i,j,0) / mf_vx(i,j,0));
595  },
596  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
597  {
598  az_ar(i,j,k) /= (mf_mx(i,j,0)*mf_my(i,j,0));
599  });
600  } // mfi
601  }
602 
603  if (solverChoice.terrain_type != TerrainType::EB) {
604 
605 #ifdef ERF_USE_FFT
606  Box my_region(subdomains[lev][isub].minimalBox());
607 #endif
608 
609  // ****************************************************************************
610  // No terrain or grid stretching
611  // ****************************************************************************
612  if (solverChoice.mesh_type == MeshType::ConstantDz) {
613  if (will_solve_with_mlmg) {
614  solve_with_mlmg(lev, rhs_sub, phi_sub, fluxes_sub, geom[lev], ref_ratio, domain_bc_type,
616  } else {
617 #ifdef ERF_USE_FFT
618  solve_with_fft(lev, isub, my_region, rhs_sub[0], phi_sub[0], fluxes_sub[0]);
619 #endif
620  }
621  } // No terrain or grid stretching
622  // ****************************************************************************
623  // Grid stretching (flat terrain)
624  // ****************************************************************************
625  else if (solverChoice.mesh_type == MeshType::StretchedDz) {
626 #ifndef ERF_USE_FFT
627  amrex::Abort("Rebuild with USE_FFT = TRUE so you can use the FFT solver");
628 #else
629  bool boxes_make_rectangle = (my_region.numPts() == subdomains[lev][isub].numPts());
630  if (!boxes_make_rectangle) {
631  amrex::Abort("FFT won't work unless the union of boxes is rectangular");
632  } else {
633  if (!use_fft) {
634  amrex::Warning("Using FFT even though you didn't set use_fft to true; it's the best choice");
635  }
636  solve_with_fft(lev, isub, my_region, rhs_sub[0], phi_sub[0], fluxes_sub[0]);
637  }
638 #endif
639  } // grid stretching
640 
641  // ****************************************************************************
642  // General terrain
643  // ****************************************************************************
644  else if (solverChoice.mesh_type == MeshType::VariableDz) {
645 #ifdef ERF_USE_FFT
646  bool boxes_make_rectangle = (my_region.numPts() == subdomains[lev][isub].numPts());
647  if (!boxes_make_rectangle) {
648  amrex::Abort("FFT preconditioner for GMRES won't work unless the union of boxes is rectangular");
649  } else {
650  solve_with_gmres(lev, my_region, rhs_sub[0], phi_sub[0], fluxes_sub[0], ax_sub, ay_sub, az_sub, dJ_sub, znd_sub);
651  }
652 #else
653  amrex::Abort("Rebuild with USE_FFT = TRUE so you can use the FFT preconditioner for GMRES");
654 #endif
655 
656  //
657  // Restore ax,ay,ax to their original definitions
658  //
659  for (MFIter mfi(rhs_lev); mfi.isValid(); ++mfi)
660  {
661  Box xbx = mfi.nodaltilebox(0);
662  Box ybx = mfi.nodaltilebox(1);
663  Box zbx = mfi.nodaltilebox(2);
664  const Array4<Real >& ax_ar = ax_sub.array(mfi);
665  const Array4<Real >& ay_ar = ay_sub.array(mfi);
666  const Array4<Real >& az_ar = az_sub.array(mfi);
667  const Array4<Real const>& mf_ux = mapfac[lev][MapFacType::u_x]->const_array(mfi);
668  const Array4<Real const>& mf_uy = mapfac[lev][MapFacType::u_y]->const_array(mfi);
669  const Array4<Real const>& mf_vx = mapfac[lev][MapFacType::v_x]->const_array(mfi);
670  const Array4<Real const>& mf_vy = mapfac[lev][MapFacType::v_y]->const_array(mfi);
671  const Array4<Real const>& mf_mx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
672  const Array4<Real const>& mf_my = mapfac[lev][MapFacType::m_y]->const_array(mfi);
674  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
675  {
676  ax_ar(i,j,k) *= (mf_uy(i,j,0) / mf_ux(i,j,0));
677  },
678  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
679  {
680  ay_ar(i,j,k) *= (mf_vx(i,j,0) / mf_vy(i,j,0));
681  },
682  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
683  {
684  az_ar(i,j,k) *= (mf_mx(i,j,0)*mf_my(i,j,0));
685  });
686  } // mfi
687 
688  } // MeshType::VariableDz
689 
690  // ****************************************************************************
691  // Print time in solve
692  // ****************************************************************************
693  Real end_step = static_cast<Real>(ParallelDescriptor::second());
694  if (mg_verbose > 0) {
695  amrex::Print() << "Time in solve " << end_step - start_step << std::endl;
696  }
697 
698  } // not EB
699  } // loop over subdomains (i)
700 
701  // ****************************************************************************
702  // When using multigrid we can solve for all of the level at once, even if there
703  // are disjoint regions
704  // ****************************************************************************
705  if (solverChoice.terrain_type == TerrainType::EB) {
706  Real start_step_eb = static_cast<Real>(ParallelDescriptor::second());
707  solve_with_EB_mlmg(lev, rhs_sub, phi_sub, fluxes_sub,
708  *(get_eb(lev).get_const_factory()),
709  *(get_eb(lev).get_u_const_factory()),
710  *(get_eb(lev).get_v_const_factory()),
711  *(get_eb(lev).get_w_const_factory()),
712  geom[lev], ref_ratio, domain_bc_type,
714  Real end_step_eb = static_cast<Real>(ParallelDescriptor::second());
715  if (mg_verbose > 0) {
716  amrex::Print() << "Time in solve " << end_step_eb - start_step_eb << std::endl;
717  }
718  }
719 
720  // ****************************************************************************
721  // Subtract dt grad(phi) from the momenta (rho0u, rho0v, Omega)
722  // ****************************************************************************
723  MultiFab::Add(mom_mf[IntVars::xmom],fluxes[0][0],0,0,1,0);
724  MultiFab::Add(mom_mf[IntVars::ymom],fluxes[0][1],0,0,1,0);
725  MultiFab::Add(mom_mf[IntVars::zmom],fluxes[0][2],0,0,1,0);
726 
727  // ****************************************************************************
728  // Define gradp from fluxes -- note that fluxes is dt * change in Gp
729  // (weighted by map factor!)
730  // ****************************************************************************
731  MultiFab::Saxpy(gradp[lev][GpVars::gpx],-one/l_dt,fluxes[0][0],0,0,1,0);
732  MultiFab::Saxpy(gradp[lev][GpVars::gpy],-one/l_dt,fluxes[0][1],0,0,1,0);
733  MultiFab::Saxpy(gradp[lev][GpVars::gpz],-one/l_dt,fluxes[0][2],0,0,1,0);
734 
735  gradp[lev][GpVars::gpx].FillBoundary(geom_tmp[0].periodicity());
736  gradp[lev][GpVars::gpy].FillBoundary(geom_tmp[0].periodicity());
737  gradp[lev][GpVars::gpz].FillBoundary(geom_tmp[0].periodicity());
738 
739  //
740  // This call is only to verify the divergence after the solve
741  // It is important we do this before computing the rho0w_arr from Omega back to rho0w
742  //
743  // ****************************************************************************
744  // THIS IS SIMPLY VERIFYING THE DIVERGENCE AFTER THE SOLVE
745  // ****************************************************************************
746  //
747  if (mg_verbose > 0)
748  {
749  rho0_u_const[0] = &mom_mf[IntVars::xmom];
750  rho0_u_const[1] = &mom_mf[IntVars::ymom];
751  rho0_u_const[2] = &mom_mf[IntVars::zmom];
752 
753  compute_divergence(lev, rhs_lev, rho0_u_const, geom_tmp[0]);
754 
755  bool local = false;
756  Real sum = volWgtSumMF(lev,rhs_lev,0,*detJ_cc[lev],*mapfac[lev][MapFacType::m_x],*mapfac[lev][MapFacType::m_y],false,local);
757 
758  if (mg_verbose > 0) {
759  Print() << "Max/L2 norm of divergence after solve at level " << lev << " : " << rhs_lev.norm0() << " " <<
760  rhs_lev.norm2() << " and volume-weighted sum " << sum << std::endl;
761  }
762 
763 #if 0
764  // FOR DEBUGGING ONLY
765  for ( MFIter mfi(rhs_lev,TilingIfNotGPU()); mfi.isValid(); ++mfi)
766  {
767  const Array4<Real const>& rhs_arr = rhs_lev.const_array(mfi);
768  Box bx = mfi.validbox();
769  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
770  if (std::abs(rhs_arr(i,j,k)) > Real(1.e-10)) {
771  amrex::AllPrint() << "RHS after solve at " <<
772  IntVect(i,j,k) << " " << rhs_arr(i,j,k) << std::endl;
773  }
774  });
775  } // mfi
776 #endif
777 
778  } // mg_verbose
779 
780  //
781  // ****************************************************************************
782  // Now convert the rho0w MultiFab back to holding (rho0w) rather than Omega
783  // ****************************************************************************
784  //
785  if (solverChoice.mesh_type == MeshType::VariableDz)
786  {
787  for (MFIter mfi(mom_mf[Vars::cons],TilingIfNotGPU()); mfi.isValid(); ++mfi)
788  {
789  Box tbz = mfi.nodaltilebox(2);
790  const Array4<Real >& rho0u_arr = mom_mf[IntVars::xmom].array(mfi);
791  const Array4<Real >& rho0v_arr = mom_mf[IntVars::ymom].array(mfi);
792  const Array4<Real >& rho0w_arr = mom_mf[IntVars::zmom].array(mfi);
793  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
794  const Array4<Real const>& mf_u = mapfac[lev][MapFacType::u_x]->const_array(mfi);
795  const Array4<Real const>& mf_v = mapfac[lev][MapFacType::v_y]->const_array(mfi);
796  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
797  Real omega = rho0w_arr(i,j,k);
798  rho0w_arr(i,j,k) = WFromOmega(i,j,k,omega,
799  rho0u_arr,rho0v_arr,
800  mf_u,mf_v,z_nd,dxInv);
801  });
802  } // mfi
803  }
804 
805  // If !fixed_density, we must convert (rho0 u) back
806  // to (rho0 u) which is what we will pass back out
807  if (!solverChoice.fixed_density[lev]) {
808  ConvertForProjection(r_hse, mom_mf[Vars::cons],
809  mom_mf[IntVars::xmom],
810  mom_mf[IntVars::ymom],
811  mom_mf[IntVars::zmom],
812  Geom(lev).Domain(),
814  }
815 
816  // ****************************************************************************
817  // Update pressure variable with phi -- note that phi is dt * change in pressure
818  // ****************************************************************************
819  MultiFab::Saxpy(pp_inc[lev], one/l_dt, phi_lev,0,0,1,1);
820 }
void ConvertForProjection(const MultiFab &den_div, const MultiFab &den_mlt, MultiFab &xmom, MultiFab &ymom, MultiFab &zmom, const Box &domain, const Vector< BCRec > &domain_bcs_type_h)
Definition: ERF_ConvertForProjection.cpp:25
void enforceInOutSolvability(int, Array< MultiFab *, AMREX_SPACEDIM > &vels_vec, Array< MultiFab *, AMREX_SPACEDIM > &area_vec, const Geometry &geom)
Definition: ERF_ConvertForProjection.cpp:326
void solve_with_mlmg(int lev, Vector< amrex::MultiFab > &rhs, Vector< MultiFab > &p, Vector< amrex::Array< MultiFab, AMREX_SPACEDIM >> &fluxes, const Geometry &geom, const amrex::Vector< amrex::IntVect > &ref_ratio, Array< std::string, 2 *AMREX_SPACEDIM > l_domain_bc_type, int mg_verbose, Real reltol, Real abstol)
void solve_with_EB_mlmg(int lev, Vector< amrex::MultiFab > &rhs, Vector< MultiFab > &p, Vector< amrex::Array< MultiFab, AMREX_SPACEDIM >> &fluxes, EBFArrayBoxFactory const &ebfact, eb_aux_ const &ebfact_u, eb_aux_ const &ebfact_v, eb_aux_ const &ebfact_w, const Geometry &geom, const amrex::Vector< amrex::IntVect > &ref_ratio, Array< std::string, 2 *AMREX_SPACEDIM > l_domain_bc_type, int mg_verbose, Real reltol, Real abstol)
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real OmegaFromW(int &i, int &j, int &k, amrex::Real w, const amrex::Array4< const amrex::Real > &u_arr, const amrex::Array4< const amrex::Real > &v_arr, const amrex::Array4< const amrex::Real > &mf_u, const amrex::Array4< const amrex::Real > &mf_v, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv)
Definition: ERF_TerrainMetrics.H:414
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real WFromOmega(int &i, int &j, int &k, amrex::Real omega, const amrex::Array4< const amrex::Real > &u_arr, const amrex::Array4< const amrex::Real > &v_arr, const amrex::Array4< const amrex::Real > &mf_u, const amrex::Array4< const amrex::Real > &mf_v, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv)
Definition: ERF_TerrainMetrics.H:464
static bool use_fft
Definition: ERF.H:1307
void solve_with_gmres(int lev, const amrex::Box &subdomain, amrex::MultiFab &rhs, amrex::MultiFab &p, amrex::Array< amrex::MultiFab, AMREX_SPACEDIM > &fluxes, amrex::MultiFab &ax_sub, amrex::MultiFab &ay_sub, amrex::MultiFab &az_sub, amrex::MultiFab &, amrex::MultiFab &znd_sub)
Definition: ERF_SolveWithGMRES.cpp:12
void compute_divergence(int lev, amrex::MultiFab &rhs, amrex::Array< amrex::MultiFab const *, AMREX_SPACEDIM > rho0_u_const, amrex::Geometry const &geom_at_lev)
Definition: ERF_ComputeDivergence.cpp:10
amrex::Real volWgtSumMF(int lev, const amrex::MultiFab &mf, int comp, const amrex::MultiFab &dJ, const amrex::MultiFab &mfx, const amrex::MultiFab &mfy, bool finemask, bool local=true)
Definition: ERF_VolWgtSum.cpp:20
@ omega
Definition: ERF_Morrison.H:53
Here is the call graph for this function:

◆ project_velocity_tb()

void ERF::project_velocity_tb ( int  lev,
amrex::Real  dt,
amrex::Vector< amrex::MultiFab > &  vars 
)

Project the single-level velocity field to enforce incompressibility with a thin body

23 {
24  BL_PROFILE("ERF::project_velocity_tb()");
25  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type == MeshType::ConstantDz);
26 
27  // Make sure the solver only sees the levels over which we are solving
28  Vector<BoxArray> ba_tmp; ba_tmp.push_back(vmf[Vars::cons].boxArray());
29  Vector<DistributionMapping> dm_tmp; dm_tmp.push_back(vmf[Vars::cons].DistributionMap());
30  Vector<Geometry> geom_tmp; geom_tmp.push_back(geom[lev]);
31 
32  // Use the default settings
33  LPInfo info;
34  std::unique_ptr<MLPoisson> p_mlpoisson;
35 #if 0
36  if (overset_imask[0]) {
37  // Add overset mask around thin body
38  p_mlpoisson = std::make_unique<MLPoisson>(geom, grids, dmap, GetVecOfConstPtrs(overset_imask), info);
39  }
40  else
41 #endif
42  {
43  // Use the default settings
44  p_mlpoisson = std::make_unique<MLPoisson>(geom_tmp, ba_tmp, dm_tmp, info);
45  }
46 
47  auto bclo = get_lo_projection_bc(geom[lev],domain_bc_type);
48  auto bchi = get_hi_projection_bc(geom[lev],domain_bc_type);
49 
50  bool need_adjust_rhs = (projection_has_dirichlet(bclo) || projection_has_dirichlet(bchi)) ? false : true;
51  p_mlpoisson->setDomainBC(bclo, bchi);
52 
53  if (lev > 0) {
54  p_mlpoisson->setCoarseFineBC(nullptr, ref_ratio[lev-1], LinOpBCType::Neumann);
55  }
56 
57  p_mlpoisson->setLevelBC(0, nullptr);
58 
59  Vector<MultiFab> rhs;
60  Vector<MultiFab> phi;
61  Vector<Array<MultiFab,AMREX_SPACEDIM> > fluxes;
62  Vector<Array<MultiFab,AMREX_SPACEDIM> > deltaf; // f^* - f^{n-1}
63  Vector<Array<MultiFab,AMREX_SPACEDIM> > u_plus_dtdf; // u + dt*deltaf
64 
65  // Used to pass array of const MFs to ComputeDivergence
66  Array<MultiFab const*, AMREX_SPACEDIM> u;
67 
68  rhs.resize(1);
69  phi.resize(1);
70  fluxes.resize(1);
71  deltaf.resize(1);
72  u_plus_dtdf.resize(1);
73 
74  rhs[0].define(ba_tmp[0], dm_tmp[0], 1, 0);
75  phi[0].define(ba_tmp[0], dm_tmp[0], 1, 0);
76  rhs[0].setVal(0.0);
77  phi[0].setVal(0.0);
78 
79  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
80  fluxes[0][idim].define(convert(ba_tmp[0], IntVect::TheDimensionVector(idim)), dm_tmp[0], 1, 0);
81  u_plus_dtdf[0][idim].define(convert(ba_tmp[0], IntVect::TheDimensionVector(idim)), dm_tmp[0], 1, 0);
82 
83  deltaf[0][idim].define(convert(ba_tmp[0], IntVect::TheDimensionVector(idim)), dm_tmp[0], 1, 0);
84  deltaf[0][idim].setVal(0.0); // start with f^* == f^{n-1}
85  }
86 
87 #if 0
88  // DEBUG
89  u[0] = &(vmf[Vars::xvel]);
90  u[1] = &(vmf[Vars::yvel]);
91  u[2] = &(vmf[Vars::zvel]);
92  computeDivergence(rhs[0], u, geom[0]);
93  Print() << "Max norm of divergence before solve at level 0 : " << rhs[0].norm0() << std::endl;
94 #endif
95 
96  for (int itp = 0; itp < solverChoice.ncorr; ++itp)
97  {
98  // Calculate u + dt*deltaf
99  for (int idim = 0; idim < 3; ++idim) {
100  MultiFab::Copy(u_plus_dtdf[0][idim], deltaf[0][idim], 0, 0, 1, 0);
101  u_plus_dtdf[0][0].mult(-l_dt,0,1,0);
102  }
103  MultiFab::Add(u_plus_dtdf[0][0], vmf[Vars::xvel], 0, 0, 1, 0);
104  MultiFab::Add(u_plus_dtdf[0][1], vmf[Vars::yvel], 0, 0, 1, 0);
105  MultiFab::Add(u_plus_dtdf[0][2], vmf[Vars::zvel], 0, 0, 1, 0);
106 
107  u[0] = &(u_plus_dtdf[0][0]);
108  u[1] = &(u_plus_dtdf[0][1]);
109  u[2] = &(u_plus_dtdf[0][2]);
110  computeDivergence(rhs[0], u, geom_tmp[0]);
111 
112 #if 0
113  // DEBUG
114  if (itp==0) {
115  for (MFIter mfi(rhs[0], TilingIfNotGPU()); mfi.isValid(); ++mfi)
116  {
117  const Box& bx = mfi.tilebox();
118  const Array4<Real const>& divU = rhs[0].const_array(mfi);
119  const Array4<Real const>& uarr = vmf[Vars::xvel].const_array(mfi);
120  const Array4<Real const>& varr = vmf[Vars::yvel].const_array(mfi);
121  const Array4<Real const>& warr = vmf[Vars::zvel].const_array(mfi);
122  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
123  {
124  if ((i>=120) && (i<=139) && (j==0) && ((k>=127)&&(k<=128))) {
125  amrex::AllPrint() << "before project div"<<IntVect(i,j,k)<<" = "<< divU(i,j,k)
126  << " u: " << uarr(i,j,k) << " " << uarr(i+1,j,k)
127  << " v: " << varr(i,j,k) << " " << varr(i,j+1,k)
128  << " w: " << warr(i,j,k) << " " << warr(i,j,k+1)
129  << std::endl;
130  }
131  });
132  }
133  }
134 #endif
135 
136  // If all Neumann BCs, adjust RHS to make sure we can converge
137  if (need_adjust_rhs) {
138  bool local = false;
139  Real offset = volWgtSumMF(lev,rhs[0],0,*detJ_cc[lev],*mapfac[lev][MapFacType::m_x],*mapfac[lev][MapFacType::m_y],false,local);
140  // amrex::Print() << "Poisson solvability offset = " << offset << std::endl;
141  rhs[0].plus(-offset, 0, 1);
142  }
143 
144  // Initialize phi to 0
145  phi[0].setVal(0.0);
146 
147  MLMG mlmg(*p_mlpoisson);
148  int max_iter = 100;
149  mlmg.setMaxIter(max_iter);
150 
151  mlmg.setVerbose(mg_verbose);
152  //mlmg.setBottomVerbose(mg_verbose);
153 
154  // solve for dt*p
155  mlmg.solve(GetVecOfPtrs(phi),
156  GetVecOfConstPtrs(rhs),
159 
160  mlmg.getFluxes(GetVecOfArrOfPtrs(fluxes));
161 
162  // Calculate new intermediate body force with updated gradp
163  if (thin_xforce[lev]) {
164  MultiFab::Copy( deltaf[0][0], fluxes[0][0], 0, 0, 1, 0);
165  ApplyInvertedMask(deltaf[0][0], *xflux_imask[0]);
166  }
167  if (thin_yforce[lev]) {
168  MultiFab::Copy( deltaf[0][1], fluxes[0][1], 0, 0, 1, 0);
169  ApplyInvertedMask(deltaf[0][1], *yflux_imask[0]);
170  }
171  if (thin_zforce[lev]) {
172  MultiFab::Copy( deltaf[0][2], fluxes[0][2], 0, 0, 1, 0);
173  ApplyInvertedMask(deltaf[0][2], *zflux_imask[0]);
174  }
175 
176  // DEBUG
177  // for (MFIter mfi(rhs[0], TilingIfNotGPU()); mfi.isValid(); ++mfi)
178  // {
179  // const Box& bx = mfi.tilebox();
180  // const Array4<Real const>& dfz_arr = deltaf[0][2].const_array(mfi);
181  // ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
182  // {
183  // if ((i>=120) && (i<=139) && (j==0) && (k==128)) {
184  // amrex::AllPrint()
185  // << " piter" << itp
186  // << " dfz"<<IntVect(i,j,k)<<" = "<< dfz_arr(i,j,k)
187  // << std::endl;
188  // }
189  // });
190  // }
191 
192  // Update pressure variable with phi -- note that phi is change in pressure, not the full pressure
193  MultiFab::Saxpy(pp_inc[lev], one, phi[0],0,0,1,0);
194 
195  // Subtract grad(phi) from the velocity components
196  Real beta = one;
197  MultiFab::Saxpy(vmf[Vars::xvel], beta, fluxes[0][0], 0, 0, 1, 0);
198  MultiFab::Saxpy(vmf[Vars::yvel], beta, fluxes[0][1], 0, 0, 1, 0);
199  MultiFab::Saxpy(vmf[Vars::zvel], beta, fluxes[0][2], 0, 0, 1, 0);
200  if (thin_xforce[lev]) {
201  ApplyMask(vmf[Vars::xvel], *xflux_imask[0]);
202  }
203  if (thin_yforce[lev]) {
204  ApplyMask(vmf[Vars::yvel], *yflux_imask[0]);
205  }
206  if (thin_zforce[lev]) {
207  ApplyMask(vmf[Vars::zvel], *zflux_imask[0]);
208  }
209  } // itp: pressure-force iterations
210 
211  // ****************************************************************************
212  // Define gradp from fluxes -- note that fluxes is dt * change in Gp
213  // ****************************************************************************
214  MultiFab::Saxpy(gradp[lev][GpVars::gpx],-one/l_dt,fluxes[0][0],0,0,1,0);
215  MultiFab::Saxpy(gradp[lev][GpVars::gpy],-one/l_dt,fluxes[0][1],0,0,1,0);
216  MultiFab::Saxpy(gradp[lev][GpVars::gpz],-one/l_dt,fluxes[0][2],0,0,1,0);
217 
218  gradp[lev][GpVars::gpx].FillBoundary(geom_tmp[0].periodicity());
219  gradp[lev][GpVars::gpy].FillBoundary(geom_tmp[0].periodicity());
220  gradp[lev][GpVars::gpz].FillBoundary(geom_tmp[0].periodicity());
221 
222  // Subtract grad(phi) from the velocity components
223 // Real beta = one;
224 // for (int ilev = lev_min; ilev <= lev_max; ++ilev) {
225 // MultiFab::Saxpy(vmf[Vars::xvel], beta, fluxes[0][0], 0, 0, 1, 0);
226 // MultiFab::Saxpy(vmf[Vars::yvel], beta, fluxes[0][1], 0, 0, 1, 0);
227 // MultiFab::Saxpy(vmf[Vars::zvel], beta, fluxes[0][2], 0, 0, 1, 0);
228 // if (thin_xforce[lev]) {
229 // ApplyMask(vmf[Vars::xvel], *xflux_imask[0]);
230 // }
231 // if (thin_yforce[lev]) {
232 // ApplyMask(vmf[Vars::yvel], *yflux_imask[0]);
233 // }
234 // if (thin_zforce[lev]) {
235 // ApplyMask(vmf[Vars::zvel], *zflux_imask[0]);
236 // }
237 // }
238 
239 #if 0
240  // Confirm that the velocity is now divergence free
241  u[0] = &(vmf[Vars::xvel]);
242  u[1] = &(vmf[Vars::yvel]);
243  u[2] = &(vmf[Vars::zvel]);
244  computeDivergence(rhs[0], u, geom_tmp[0]);
245  Print() << "Max norm of divergence after solve at level " << lev << " : " << rhs[0].norm0() << std::endl;
246 
247 #endif
248 }
bool projection_has_dirichlet(Array< LinOpBCType, AMREX_SPACEDIM > bcs)
Definition: ERF_PoissonSolve_tb.cpp:10
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:28
Array< LinOpBCType, AMREX_SPACEDIM > get_lo_projection_bc(Geometry const &lev_geom, Array< std::string, 2 *AMREX_SPACEDIM > l_domain_bc_type)
Definition: ERF_SolverUtils.H:13
Array< LinOpBCType, AMREX_SPACEDIM > get_hi_projection_bc(Geometry const &lev_geom, Array< std::string, 2 *AMREX_SPACEDIM > l_domain_bc_type)
Definition: ERF_SolverUtils.H:34
AMREX_GPU_HOST AMREX_FORCE_INLINE void ApplyInvertedMask(amrex::MultiFab &dst, const amrex::iMultiFab &imask, const int nghost=0)
Definition: ERF_Utils.H:463
Here is the call graph for this function:

◆ read_box_for_refinement()

void ERF::read_box_for_refinement ( std::string &  ref_prefix,
int &  lev_for_box,
amrex::RealBox &  real_box 
)
7 {
8  ParmParse ppr(ref_prefix);
9 
10  int num_real_lo = ppr.countval("in_box_lo");
11  int num_real_hi = ppr.countval("in_box_hi");
12 
13  int num_indx_lo = ppr.countval("in_box_lo_indices");
14  int num_indx_hi = ppr.countval("in_box_hi_indices");
15 
16  int num_indx_lo_crse = ppr.countval("in_box_lo_indices_crse");
17  int num_indx_hi_crse = ppr.countval("in_box_hi_indices_crse");
18 
19  AMREX_ALWAYS_ASSERT( (num_real_lo == num_real_hi) && (num_real_lo == 0 || num_real_lo >= 2) );
20  AMREX_ALWAYS_ASSERT( (num_indx_lo == num_indx_hi) && (num_indx_lo == 0 || num_indx_lo >= 2) );
21  AMREX_ALWAYS_ASSERT( (num_indx_lo_crse == num_indx_hi_crse) && (num_indx_lo_crse == 0 || num_indx_lo_crse >= 2) );
22 
23  // Problem low and high (in real not index space) are the same at all levels
24  const Real* plo = geom[0].ProbLo();
25  const Real* phi = geom[0].ProbHi();
26  if ( !((num_real_lo >= AMREX_SPACEDIM-1 && num_indx_lo == 0 && num_indx_lo_crse == 0) ||
27  (num_indx_lo >= AMREX_SPACEDIM-1 && num_real_lo == 0 && num_indx_lo_crse == 0) ||
28  (num_indx_lo == 0 && num_real_lo == 0 && num_indx_lo_crse == 0) ||
29  (num_indx_lo_crse >= AMREX_SPACEDIM-1 && num_real_lo == 0 && num_indx_lo == 0)
30  ) )
31  {
32  amrex::Abort("Must only specify box for refinement using real OR index space with fine/coarse grid indices");
33  }
34 
35  // Clear stale data
36  lev_for_box = max_level;
37  if (num_real_lo > 0) {
38  ppr.query("max_level",lev_for_box);
39  } else if (num_indx_lo > 0 || num_indx_lo_crse > 0) {
40  ppr.get("max_level",lev_for_box);
41  }
42  num_boxes_at_level[lev_for_box] = 0;
43  boxes_at_level[lev_for_box].clear();
44 
45  if (num_real_lo > 0) {
46 
47  std::vector<Real> rbox_lo(3), rbox_hi(3);
48  if (lev_for_box > 0 && lev_for_box <= max_level)
49  {
50  if (n_error_buf[0] != IntVect::TheZeroVector()) {
51  amrex::Abort("Don't use n_error_buf > 0 when setting the box explicitly");
52  }
53 
54  ppr.getarr("in_box_lo",rbox_lo,0,num_real_lo);
55  ppr.getarr("in_box_hi",rbox_hi,0,num_real_hi);
56 
57  if (rbox_lo[0] < plo[0]) rbox_lo[0] = plo[0];
58  if (rbox_lo[1] < plo[1]) rbox_lo[1] = plo[1];
59  if (rbox_hi[0] > phi[0]) rbox_hi[0] = phi[0];
60  if (rbox_hi[1] > phi[1]) rbox_hi[1] = phi[1];
61  if (num_real_lo < AMREX_SPACEDIM) {
62  rbox_lo[2] = plo[2];
63  rbox_hi[2] = phi[2];
64  }
65 
66  const Box& domain = geom[lev_for_box].Domain();
67 
68  real_box = RealBox(&(rbox_lo[0]),&(rbox_hi[0]));
69 
70  Print() << "Realbox read in and intersected laterally with domain is " << real_box << std::endl;
71 
72  num_boxes_at_level[lev_for_box] += 1;
73 
74  int ilo, jlo, klo;
75  int ihi, jhi, khi;
76  const auto* dx = geom[lev_for_box].CellSize();
77  ilo = static_cast<int>((rbox_lo[0] - plo[0])/dx[0]);
78  jlo = static_cast<int>((rbox_lo[1] - plo[1])/dx[1]);
79  ihi = static_cast<int>((rbox_hi[0] - plo[0])/dx[0]-1);
80  jhi = static_cast<int>((rbox_hi[1] - plo[1])/dx[1]-1);
81  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
82  // Search for k indices corresponding to nominal grid
83  // AGL heights
84  klo = domain.smallEnd(2) - 1;
85  khi = domain.smallEnd(2) - 1;
86 
87  if (rbox_lo[2] <= zlevels_stag[lev_for_box][domain.smallEnd(2)])
88  {
89  klo = domain.smallEnd(2);
90  }
91  else
92  {
93  for (int k=domain.smallEnd(2); k<=domain.bigEnd(2)+1; ++k) {
94  if (zlevels_stag[lev_for_box][k] > rbox_lo[2]) {
95  klo = k-1;
96  break;
97  }
98  }
99  }
100  AMREX_ASSERT(klo >= domain.smallEnd(2));
101 
102  if (rbox_hi[2] >= zlevels_stag[lev_for_box][domain.bigEnd(2)+1])
103  {
104  khi = domain.bigEnd(2);
105  }
106  else
107  {
108  for (int k=klo+1; k<=domain.bigEnd(2)+1; ++k) {
109  if (zlevels_stag[lev_for_box][k] > rbox_hi[2]) {
110  khi = k-1;
111  break;
112  }
113  }
114  }
115  AMREX_ASSERT((khi <= domain.bigEnd(2)) && (khi > klo));
116 
117  // Need to update real_box because tagging is based on
118  // the initial _un_deformed grid
119  real_box = RealBox(plo[0]+ ilo *dx[0], plo[1]+ jlo *dx[1], plo[2]+ klo *dx[2],
120  plo[0]+(ihi+1)*dx[0], plo[1]+(jhi+1)*dx[1], plo[2]+(khi+1)*dx[2]);
121  } else {
122  klo = static_cast<int>((rbox_lo[2] - plo[2])/dx[2]);
123  khi = static_cast<int>((rbox_hi[2] - plo[2])/dx[2]-1);
124  }
125 
126  // Snap box indices to ref_ratio alignment (round lo down, hi up)
127  {
128  const auto& rr = ref_ratio[lev_for_box-1];
129  auto snap_lo = [](int idx, int r) { return idx - (idx % r + r) % r; };
130  auto snap_hi = [](int idx_p1, int r) { // idx_p1 = ihi+1
131  int rem = idx_p1 % r;
132  return (rem == 0) ? idx_p1 - 1 : idx_p1 + (r - rem) - 1;
133  };
134  int ilo_old = ilo, jlo_old = jlo, klo_old = klo;
135  int ihi_old = ihi, jhi_old = jhi, khi_old = khi;
136  ilo = snap_lo(ilo, rr[0]);
137  jlo = snap_lo(jlo, rr[1]);
138  klo = snap_lo(klo, rr[2]);
139  ihi = snap_hi(ihi+1, rr[0]);
140  jhi = snap_hi(jhi+1, rr[1]);
141  khi = snap_hi(khi+1, rr[2]);
142  if (ilo != ilo_old || ihi != ihi_old ||
143  jlo != jlo_old || jhi != jhi_old ||
144  klo != klo_old || khi != khi_old) {
145  amrex::Print() << "Refinement box indices snapped to ref_ratio alignment:\n"
146  << " ilo: " << ilo_old << " -> " << ilo
147  << " ihi: " << ihi_old << " -> " << ihi
148  << " jlo: " << jlo_old << " -> " << jlo
149  << " jhi: " << jhi_old << " -> " << jhi
150  << " klo: " << klo_old << " -> " << klo
151  << " khi: " << khi_old << " -> " << khi << "\n";
152  }
153  }
154 
155  Box bx(IntVect(ilo,jlo,klo),IntVect(ihi,jhi,khi));
156 
157  bool using_pbl = (solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYJ ||
158  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYNN25 ||
159  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYNNEDMF ||
160  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::YSU ||
161  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MRF);
162 
163  if ( using_pbl && ( (rbox_lo[2] > plo[2]) || (rbox_hi[2] < phi[2]) ) ) {
164  amrex::Print() << "PBL models need refinement boxes that go from the bottom to the top of the domain for calculation of PBLH" << std::endl;
165  amrex::Print() << "Please set in_box_lo to geometry.prob_lo in z and in_box_hi to geometry.prob_hi in z and try again" << std::endl;
166  amrex::Abort();
167  }
168 
169  boxes_at_level[lev_for_box].push_back(bx);
170  Print() << "Saving in 'boxes at level' as " << bx << std::endl;
171  } // (lev_for_box > 0 && lev_for_box <= max_level)
172 
173  if (solverChoice.init_type == InitType::WRFInput) {
174  amrex::Print() << "Size of num_boxes " << num_boxes_at_level.size() << std::endl;
175  amrex::Print() << "Size of num_files " << num_files_at_level.size() << std::endl;
176  amrex::Print() << "Consider lev_for_box = " << lev_for_box << std::endl;
177  amrex::Print() << "Number of boxes at level " << num_boxes_at_level[lev_for_box] << std::endl;
178  amrex::Print() << "Number of available files " << num_files_at_level[lev_for_box] << std::endl;
179  if (num_boxes_at_level[lev_for_box] != num_files_at_level[lev_for_box]) {
180  amrex::Print() << "Will need to rely on refinement criteria from inputs file" << std::endl;
181  }
182  }
183 
184  } else if (num_indx_lo > 0) {
185 
186  std::vector<int> box_lo(3), box_hi(3);
187  if (lev_for_box > 0 && lev_for_box <= max_level)
188  {
189  if (n_error_buf[0] != IntVect::TheZeroVector()) {
190  amrex::Abort("Don't use n_error_buf > 0 when setting the box explicitly");
191  }
192 
193  ppr.getarr("in_box_lo_indices",box_lo,0,num_indx_lo);
194  ppr.getarr("in_box_hi_indices",box_hi,0,num_indx_hi);
195 
196  if (num_indx_lo < AMREX_SPACEDIM) {
197  box_lo[2] = geom[lev_for_box].Domain().smallEnd(2);
198  box_hi[2] = geom[lev_for_box].Domain().bigEnd(2);
199  }
200 
201  Box bx(IntVect(box_lo[0],box_lo[1],box_lo[2]),IntVect(box_hi[0],box_hi[1],box_hi[2]));
202  const Box& domain = geom[lev_for_box].Domain();
203 
204  if (!domain.contains(bx)) {
205  amrex::Print() << "\n";
206  amrex::Print() << "Box specified is " << bx << std::endl;
207  amrex::Print() << "But domain at level is " << domain << std::endl;
208  amrex::Error("Specified box doesn't fit in the domain");
209  }
210 
211  const auto* dx = geom[lev_for_box].CellSize();
212  real_box = RealBox(plo[0]+ box_lo[0] *dx[0], plo[1]+ box_lo[1] *dx[1], plo[2]+ box_lo[2] *dx[2],
213  plo[0]+(box_hi[0]+1)*dx[0], plo[1]+(box_hi[1]+1)*dx[1], plo[2]+(box_hi[2]+1)*dx[2]);
214 
215  Print() << "Reading " << bx << " at level " << lev_for_box << std::endl;
216  num_boxes_at_level[lev_for_box] += 1;
217 
218  // Snap box indices to ref_ratio alignment (round lo down, hi up)
219  {
220  const auto& rr = ref_ratio[lev_for_box-1];
221  auto snap_lo_fn = [](int idx, int r) { return idx - (idx % r + r) % r; };
222  auto snap_hi_fn = [](int idx_p1, int r) {
223  int rem = idx_p1 % r;
224  return (rem == 0) ? idx_p1 - 1 : idx_p1 + (r - rem) - 1;
225  };
226  int lo_old[3] = {box_lo[0], box_lo[1], box_lo[2]};
227  int hi_old[3] = {box_hi[0], box_hi[1], box_hi[2]};
228  box_lo[0] = snap_lo_fn(box_lo[0], rr[0]);
229  box_lo[1] = snap_lo_fn(box_lo[1], rr[1]);
230  box_lo[2] = snap_lo_fn(box_lo[2], rr[2]);
231  box_hi[0] = snap_hi_fn(box_hi[0]+1, rr[0]);
232  box_hi[1] = snap_hi_fn(box_hi[1]+1, rr[1]);
233  box_hi[2] = snap_hi_fn(box_hi[2]+1, rr[2]);
234  if (box_lo[0] != lo_old[0] || box_hi[0] != hi_old[0] ||
235  box_lo[1] != lo_old[1] || box_hi[1] != hi_old[1] ||
236  box_lo[2] != lo_old[2] || box_hi[2] != hi_old[2]) {
237  amrex::Print() << "Refinement box indices snapped to ref_ratio alignment:\n"
238  << " ilo: " << lo_old[0] << " -> " << box_lo[0]
239  << " ihi: " << hi_old[0] << " -> " << box_hi[0]
240  << " jlo: " << lo_old[1] << " -> " << box_lo[1]
241  << " jhi: " << hi_old[1] << " -> " << box_hi[1]
242  << " klo: " << lo_old[2] << " -> " << box_lo[2]
243  << " khi: " << hi_old[2] << " -> " << box_hi[2] << "\n";
244  }
245  bx = Box(IntVect(box_lo[0],box_lo[1],box_lo[2]),
246  IntVect(box_hi[0],box_hi[1],box_hi[2]));
247  }
248 
249  bool using_pbl = (solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYJ ||
250  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYNN25 ||
251  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYNNEDMF ||
252  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::YSU ||
253  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MRF);
254 
255  if ( using_pbl && ( (box_lo[2] > 0) || (box_hi[2] < domain.bigEnd(2)) ) ) {
256  amrex::Print() << "PBL models need refinement boxes that go from the bottom to the top of the domain for calculation of PBLH" << std::endl;
257  amrex::Print() << "Please set in_box_lo_indices to 0 in z and in_box_hi_indices to amr.n_cell-1 in z and try again" << std::endl;
258  amrex::Abort();
259  }
260 
261  boxes_at_level[lev_for_box].push_back(bx);
262  Print() << "Saving in 'boxes at level' as " << bx << std::endl;
263  } // lev
264 
265  if (solverChoice.init_type == InitType::WRFInput) {
266  if ( (num_files_at_level[lev_for_box] > 0) &&
267  (num_boxes_at_level[lev_for_box] != num_files_at_level[lev_for_box]) ) {
268  amrex::Error("Number of boxes doesn't match number of input files");
269 
270  }
271  }
272 
273  } else if (num_indx_lo_crse > 0) {
274 
275  std::vector<int> box_lo(3), box_hi(3);
276  if (lev_for_box > 0 && lev_for_box <= max_level)
277  {
278  if (n_error_buf[0] != IntVect::TheZeroVector()) {
279  amrex::Abort("Don't use n_error_buf > 0 when setting the box explicitly");
280  }
281 
282  ppr.getarr("in_box_lo_indices_crse",box_lo,0,num_indx_lo_crse);
283  ppr.getarr("in_box_hi_indices_crse",box_hi,0,num_indx_hi_crse);
284 
285  if (num_indx_lo_crse < AMREX_SPACEDIM) {
286  box_lo[2] = geom[lev_for_box-1].Domain().smallEnd(2);
287  box_hi[2] = geom[lev_for_box-1].Domain().bigEnd(2);
288  }
289 
290  Box bx(IntVect(box_lo[0],box_lo[1],box_lo[2]),IntVect(box_hi[0],box_hi[1],box_hi[2]));
291 
292  if (!geom[lev_for_box-1].Domain().contains(bx)) {
293  amrex::Print() << "\n";
294  amrex::Print() << "(Coarse) Box specified is " << bx << std::endl;
295  amrex::Print() << "But (coarse) domain at level is " << geom[lev_for_box-1].Domain() << std::endl;
296  amrex::Error("Specified box doesn't fit in the domain");
297  }
298 
299  bx.refine(ref_ratio[lev_for_box-1]);
300 
301  const auto* dx = geom[lev_for_box-1].CellSize();
302 
303  real_box = RealBox(plo[0]+ box_lo[0] *dx[0], plo[1]+ box_lo[1] *dx[1], plo[2]+ box_lo[2] *dx[2],
304  plo[0]+(box_hi[0]+1)*dx[0], plo[1]+(box_hi[1]+1)*dx[1], plo[2]+(box_hi[2]+1)*dx[2]);
305 
306  Print() << "Reading " << bx << " at level " << lev_for_box << std::endl;
307  num_boxes_at_level[lev_for_box] += 1;
308  bool using_pbl = (solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYJ ||
309  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYNN25 ||
310  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MYNNEDMF ||
311  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::YSU ||
312  solverChoice.turbChoice[lev_for_box].pbl_type == PBLType::MRF);
313 
314  const Box& domain = geom[lev_for_box].Domain();
315  if ( using_pbl && ( (box_lo[2] > 0) || (box_hi[2] < domain.bigEnd(2)) ) ) {
316  amrex::Print() << "PBL models need refinement boxes that go from the bottom to the top of the domain for calculation of PBLH" << std::endl;
317  amrex::Print() << "Please set in_box_lo_indices_crse to 0 in z and in_box_hi_indices_crse to amr.n_cell-1 in z and try again" << std::endl;
318  amrex::Abort();
319  }
320 
321  boxes_at_level[lev_for_box].push_back(bx);
322  Print() << "Saving in 'boxes at level' as " << bx << std::endl;
323  } // lev
324 
325  if (solverChoice.init_type == InitType::WRFInput) {
326  if ( (num_files_at_level[lev_for_box] > 0) &&
327  (num_boxes_at_level[lev_for_box] != num_files_at_level[lev_for_box]) ) {
328  amrex::Error("Number of boxes doesn't match number of input files");
329 
330  }
331  }
332  }
333 }
amrex::Vector< int > num_files_at_level
Definition: ERF.H:889
real(c_double), private rr
Definition: ERF_module_mp_morr_two_moment.F90:224
Here is the call graph for this function:

◆ ReadCheckpointFile()

void ERF::ReadCheckpointFile ( )

ERF function for reading data from a checkpoint file during restart.

505 {
506  Print() << "Restart from native checkpoint " << restart_chkfile << "\n";
507 
508  // Header
509  std::string File(restart_chkfile + "/Header");
510 
511  VisMF::IO_Buffer io_buffer(VisMF::GetIOBufferSize());
512 
513  Vector<char> fileCharPtr;
514  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
515  std::string fileCharPtrString(fileCharPtr.dataPtr());
516  std::istringstream is(fileCharPtrString, std::istringstream::in);
517 
518  std::string line, word;
519 
520  int chk_ncomp_cons, chk_ncomp;
521 
522  // read in title line
523  std::getline(is, line);
524 
525  // read in finest_level
526  is >> finest_level;
527  GotoNextLine(is);
528 
529  // read the number of components
530  // for each variable we store
531 
532  // conservative, cell-centered vars
533  is >> chk_ncomp_cons;
534  GotoNextLine(is);
535 
536  // x-velocity on faces
537  is >> chk_ncomp;
538  GotoNextLine(is);
539  AMREX_ASSERT(chk_ncomp == 1);
540 
541  // y-velocity on faces
542  is >> chk_ncomp;
543  GotoNextLine(is);
544  AMREX_ASSERT(chk_ncomp == 1);
545 
546  // z-velocity on faces
547  is >> chk_ncomp;
548  GotoNextLine(is);
549  AMREX_ASSERT(chk_ncomp == 1);
550 
551  // read in array of istep
552  std::getline(is, line);
553  {
554  std::istringstream lis(line);
555  int i = 0;
556  while (lis >> word) {
557  istep[i++] = std::stoi(word);
558  }
559  }
560 
561  // read in array of dt
562  std::getline(is, line);
563  {
564  std::istringstream lis(line);
565  int i = 0;
566  while (lis >> word) {
567  dt[i++] = static_cast<Real>(std::stod(word));
568  }
569  }
570 
571  // read in array of t_new
572  std::getline(is, line);
573  {
574  std::istringstream lis(line);
575  int i = 0;
576  while (lis >> word) {
577  t_new[i++] = static_cast<Real>(std::stod(word));
578  }
579  }
580 
581  for (int lev = 0; lev <= finest_level; ++lev) {
582  // read in level 'lev' BoxArray from Header
583  BoxArray ba;
584  ba.readFrom(is);
585  GotoNextLine(is);
586 
587  // create a distribution mapping
588  DistributionMapping dm { ba, ParallelDescriptor::NProcs() };
589 
590  MakeNewLevelFromScratch (lev, t_new[lev], ba, dm);
591  }
592 
593  // ncomp is only valid after we MakeNewLevelFromScratch (asks micro how many vars)
594  // NOTE: Data is written over ncomp, so check that we match the header file
595  int ncomp_cons = vars_new[0][Vars::cons].nComp();
596 
597  // NOTE: QKE was removed so this is for backward compatibility
598  AMREX_ASSERT((chk_ncomp_cons==ncomp_cons) || ((chk_ncomp_cons-1)==ncomp_cons));
599  //
600  // See if we have a written separate file that tells how many components and how many ghost cells
601  // we have of the base state
602  //
603  // If we can't find the file, then set the number of components to the original number = 3
604  //
605  int ncomp_base_to_read = 3;
606  IntVect ng_base = IntVect{1};
607  {
608  std::string BaseStateFile(restart_chkfile + "/num_base_state_comps");
609 
610  if (amrex::FileExists(BaseStateFile))
611  {
612  Vector<char> BaseStatefileCharPtr;
613  ParallelDescriptor::ReadAndBcastFile(BaseStateFile, BaseStatefileCharPtr);
614  std::string BaseStatefileCharPtrString(BaseStatefileCharPtr.dataPtr());
615 
616  // We set this to the default value of 3 but allow it be larger if th0 and qv0 were written
617  std::istringstream isb(BaseStatefileCharPtrString, std::istringstream::in);
618  isb >> ncomp_base_to_read;
619  isb >> ng_base;
620  }
621  }
622 
623  // read in the MultiFab data
624  for (int lev = 0; lev <= finest_level; ++lev)
625  {
626  // NOTE: For backward compatibility (chk file has QKE)
627  if ((chk_ncomp_cons-1)==ncomp_cons) {
628  MultiFab cons(grids[lev],dmap[lev],chk_ncomp_cons,0);
629  VisMF::Read(cons, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Cell"));
630 
631  // Copy up to RhoKE_comp
632  MultiFab::Copy(vars_new[lev][Vars::cons],cons,0,0,(RhoKE_comp+1),0);
633 
634  // Only if we have a PBL model do we need to copy QKE is src to KE in dst
635  if ( (solverChoice.turbChoice[lev].pbl_type == PBLType::MYNN25) ||
636  (solverChoice.turbChoice[lev].pbl_type == PBLType::MYNNEDMF) ) {
637  MultiFab::Copy(vars_new[lev][Vars::cons],cons,(RhoKE_comp+1),RhoKE_comp,1,0);
638  vars_new[lev][Vars::cons].mult(myhalf,RhoKE_comp,1,0);
639  }
640 
641  // Copy other components
642  int ncomp_remainder = ncomp_cons - (RhoKE_comp + 1);
643  MultiFab::Copy(vars_new[lev][Vars::cons],cons,(RhoKE_comp+2),(RhoKE_comp+1),ncomp_remainder,0);
644 
645  vars_new[lev][Vars::cons].setBndry(bogus_large_value);
646  } else {
647  MultiFab cons(grids[lev],dmap[lev],ncomp_cons,0);
648  VisMF::Read(cons, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Cell"));
649  MultiFab::Copy(vars_new[lev][Vars::cons],cons,0,0,ncomp_cons,0);
650  vars_new[lev][Vars::cons].setBndry(bogus_large_value);
651  }
652 
653  MultiFab xvel(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
654  VisMF::Read(xvel, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "XFace"));
655  MultiFab::Copy(vars_new[lev][Vars::xvel],xvel,0,0,1,0);
656  vars_new[lev][Vars::xvel].setBndry(bogus_large_value);
657 
658  MultiFab yvel(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
659  VisMF::Read(yvel, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "YFace"));
660  MultiFab::Copy(vars_new[lev][Vars::yvel],yvel,0,0,1,0);
661  vars_new[lev][Vars::yvel].setBndry(bogus_large_value);
662 
663  MultiFab zvel(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
664  VisMF::Read(zvel, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "ZFace"));
665  MultiFab::Copy(vars_new[lev][Vars::zvel],zvel,0,0,1,0);
666  vars_new[lev][Vars::zvel].setBndry(bogus_large_value);
667 
668  if (solverChoice.anelastic[lev] == 1) {
669  MultiFab ppinc(grids[lev],dmap[lev],1,0);
670  VisMF::Read(ppinc, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "PP_Inc"));
671  MultiFab::Copy(pp_inc[lev],ppinc,0,0,1,0);
672  pp_inc[lev].FillBoundary(geom[lev].periodicity());
673 
674  MultiFab gpx(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
675  VisMF::Read(gpx, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Gpx"));
676  MultiFab::Copy(gradp[lev][GpVars::gpx],gpx,0,0,1,0);
677  gradp[lev][GpVars::gpx].FillBoundary(geom[lev].periodicity());
678 
679  MultiFab gpy(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
680  VisMF::Read(gpy, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Gpy"));
681  MultiFab::Copy(gradp[lev][GpVars::gpy],gpy,0,0,1,0);
682  gradp[lev][GpVars::gpy].FillBoundary(geom[lev].periodicity());
683 
684  MultiFab gpz(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
685  VisMF::Read(gpz, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Gpz"));
686  MultiFab::Copy(gradp[lev][GpVars::gpz],gpz,0,0,1,0);
687  gradp[lev][GpVars::gpz].FillBoundary(geom[lev].periodicity());
688  }
689 
690  // Note that we read the ghost cells of the base state (unlike above)
691 
692  // The original base state only had 3 components and 1 ghost cell -- we read this
693  // here to be consistent with the old style
694  MultiFab base(grids[lev],dmap[lev],ncomp_base_to_read,ng_base);
695  VisMF::Read(base, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "BaseState"));
696 
697  MultiFab::Copy(base_state[lev],base,0,0,ncomp_base_to_read,ng_base);
698 
699  // Create theta0 from p0, rh0
700  if (ncomp_base_to_read < 4) {
701  for (MFIter mfi(base_state[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
702  {
703  // We only compute theta_0 on valid cells since we will impose domain BC's after restart
704  const Box& bx = mfi.tilebox();
705  Array4<Real> const& fab = base_state[lev].array(mfi);
706  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
707  {
709  / fab(i,j,k,BaseState::r0_comp);
710  });
711  }
712  }
713  // Default theta0 to 0
714  if (ncomp_base_to_read < 5) {
715  for (MFIter mfi(base_state[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
716  {
717  // We only compute theta_0 on valid cells since we will impose domain BC's after restart
718  const Box& bx = mfi.tilebox();
719  Array4<Real> const& fab = base_state[lev].array(mfi);
720  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
721  {
722  fab(i,j,k,BaseState::qv0_comp) = zero;
723  });
724  }
725  }
726  base_state[lev].FillBoundary(geom[lev].periodicity());
727 
728  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
729  // Note that we also read the ghost cells of z_phys_nd
730  IntVect ng = z_phys_nd[lev]->nGrowVect();
731  MultiFab z_height(convert(grids[lev],IntVect(1,1,1)),dmap[lev],1,ng);
732  VisMF::Read(z_height, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Z_Phys_nd"));
733  MultiFab::Copy(*z_phys_nd[lev],z_height,0,0,1,ng);
735 
736  // Compute the min dz and pass to the micro model
737  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
738  micro->Set_dzmin(lev, dzmin);
739 
740  if ( (solverChoice.init_type != InitType::WRFInput) && (solverChoice.init_type != InitType::Metgrid) ) {
741  check_mesh_type(lev);
742  }
743  }
744 
745  // Read in the moisture model restart variables
746  std::vector<int> qmoist_indices;
747  std::vector<std::string> qmoist_names;
748  micro->Get_Qmoist_Restart_Vars(lev, solverChoice, qmoist_indices, qmoist_names);
749  int qmoist_nvar = static_cast<int>(qmoist_indices.size());
750  for (int var = 0; var < qmoist_nvar; var++) {
751  const int ncomp = 1;
752  IntVect ng_moist = qmoist[lev][qmoist_indices[var]]->nGrowVect();
753  MultiFab moist_vars(grids[lev],dmap[lev],ncomp,ng_moist);
754  VisMF::Read(moist_vars, amrex::MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", qmoist_names[var]));
755  MultiFab::Copy(*(qmoist[lev][qmoist_indices[var]]),moist_vars,0,0,ncomp,ng_moist);
756  }
757 
758 #if defined(ERF_USE_WINDFARM)
759  if(solverChoice.windfarm_type == WindFarmType::Fitch or
760  solverChoice.windfarm_type == WindFarmType::EWP or
761  solverChoice.windfarm_type == WindFarmType::SimpleAD){
762  IntVect ng = Nturb[lev].nGrowVect();
763  MultiFab mf_Nturb(grids[lev],dmap[lev],1,ng);
764  VisMF::Read(mf_Nturb, amrex::MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "NumTurb"));
765  MultiFab::Copy(Nturb[lev],mf_Nturb,0,0,1,ng);
766  }
767 #endif
768 
769  // Read the LSM data
770  if (solverChoice.lsm_type != LandSurfaceType::None) {
771  amrex::Print() << "Reading LSM variables" << std::endl;
772  for (int ivar(0); ivar<lsm_data[lev].size(); ++ivar) {
773  BoxArray ba = lsm_data[lev][ivar]->boxArray();
774  DistributionMapping dm = lsm_data[lev][ivar]->DistributionMap();
775  IntVect ng = lsm_data[lev][ivar]->nGrowVect();
776  int nvar = lsm_data[lev][ivar]->nComp();
777  MultiFab lsm_vars(ba,dm,nvar,ng);
778  VisMF::Read(lsm_vars, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LsmData" + std::to_string(ivar)));
779  MultiFab::Copy(*(lsm_data[lev][ivar]),lsm_vars,0,0,nvar,ng);
780  }
781  for (int iflux(0); iflux<lsm_flux[lev].size(); ++iflux) {
782  BoxArray ba = lsm_flux[lev][iflux]->boxArray();
783  DistributionMapping dm = lsm_flux[lev][iflux]->DistributionMap();
784  IntVect ng = lsm_flux[lev][iflux]->nGrowVect();
785  int nvar = lsm_flux[lev][iflux]->nComp();
786  MultiFab lsm_vars(ba,dm,nvar,ng);
787  VisMF::Read(lsm_vars, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LsmFlux" + std::to_string(iflux)));
788  MultiFab::Copy(*(lsm_flux[lev][iflux]),lsm_vars,0,0,nvar,ng);
789  }
790  }
791 
792  // Read the radiation heating rates
793  std::string RadFileName(restart_chkfile + "/Level_0/Qrad_H");
794  if ((solverChoice.rad_type != RadiationType::None) && amrex::FileExists(RadFileName)) {
795  amrex::Print() << "Reading radiation heating rates" << std::endl;
796  int nrad = qheating_rates[lev]->nComp();
797  MultiFab mf_rad(grids[lev],dmap[lev],nrad,0);
798  VisMF::Read(mf_rad, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Qrad"));
799  MultiFab::Copy(*qheating_rates[lev],mf_rad,0,0,nrad,0);
800  }
801 
802  IntVect ng = mapfac[lev][MapFacType::m_x]->nGrowVect();
803  MultiFab mf_m(ba2d[lev],dmap[lev],1,ng);
804 
805  std::string MapFacMFileName(restart_chkfile + "/Level_0/MapFactor_mx_H");
806  if (amrex::FileExists(MapFacMFileName)) {
807  VisMF::Read(mf_m, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_mx"));
808  } else {
809  VisMF::Read(mf_m, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_m"));
810  }
811  MultiFab::Copy(*mapfac[lev][MapFacType::m_x],mf_m,0,0,1,ng);
812 
813 #if 0
815  VisMF::Read(mf_m, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_my"));
816  MultiFab::Copy(*mapfac[lev][MapFacType::m_y],mf_m,0,0,1,ng);
817  }
818 #endif
819 
820  ng = mapfac[lev][MapFacType::u_x]->nGrowVect();
821  MultiFab mf_u(convert(ba2d[lev],IntVect(1,0,0)),dmap[lev],1,ng);
822 
823  std::string MapFacUFileName(restart_chkfile + "/Level_0/MapFactor_ux_H");
824  if (amrex::FileExists(MapFacUFileName)) {
825  VisMF::Read(mf_u, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_ux"));
826  } else {
827  VisMF::Read(mf_u, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_u"));
828  }
829  MultiFab::Copy(*mapfac[lev][MapFacType::u_x],mf_u,0,0,1,ng);
830 
831 #if 0
833  VisMF::Read(mf_u, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_uy"));
834  MultiFab::Copy(*mapfac[lev][MapFacType::u_y],mf_u,0,0,1,ng);
835  }
836 #endif
837 
838  ng = mapfac[lev][MapFacType::v_x]->nGrowVect();
839  MultiFab mf_v(convert(ba2d[lev],IntVect(0,1,0)),dmap[lev],1,ng);
840 
841  std::string MapFacVFileName(restart_chkfile + "/Level_0/MapFactor_vx_H");
842  if (amrex::FileExists(MapFacVFileName)) {
843  VisMF::Read(mf_v, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_vx"));
844  } else {
845  VisMF::Read(mf_v, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_v"));
846  }
847  MultiFab::Copy(*mapfac[lev][MapFacType::v_x],mf_v,0,0,1,ng);
848 
849 #if 0
851  VisMF::Read(mf_v, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_vy"));
852  MultiFab::Copy(*mapfac[lev][MapFacType::v_y],mf_v,0,0,1,ng);
853  }
854 #endif
855 
856 
857  // NOTE: We read MOST data in ReadCheckpointFileMOST (see below)!
858 
859  // See if we wrote out SST data
860  std::string FirstSSTFileName(restart_chkfile + "/Level_0/SST_0_H");
861  if (amrex::FileExists(FirstSSTFileName))
862  {
863  amrex::Print() << "Reading SST data" << std::endl;
864  int ntimes = 1;
865  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
866  MultiFab sst_at_t(ba2d[lev],dmap[lev],1,ng);
867  sst_lev[lev][0] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ng);
868  for (int nt(0); nt<ntimes; ++nt) {
869  VisMF::Read(sst_at_t, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_",
870  "SST_" + std::to_string(nt)));
871  MultiFab::Copy(*sst_lev[lev][nt],sst_at_t,0,0,1,ng);
872  }
873  }
874 
875  // See if we wrote out TSK data
876  std::string FirstTSKFileName(restart_chkfile + "/Level_0/TSK_0_H");
877  if (amrex::FileExists(FirstTSKFileName))
878  {
879  amrex::Print() << "Reading TSK data" << std::endl;
880  int ntimes = 1;
881  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
882  MultiFab tsk_at_t(ba2d[lev],dmap[lev],1,ng);
883  tsk_lev[lev][0] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ng);
884  for (int nt(0); nt<ntimes; ++nt) {
885  VisMF::Read(tsk_at_t, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_",
886  "TSK_" + std::to_string(nt)));
887  MultiFab::Copy(*tsk_lev[lev][nt],tsk_at_t,0,0,1,ng);
888  }
889  }
890 
891  std::string LMaskFileName(restart_chkfile + "/Level_0/LMASK_0_H");
892  if (amrex::FileExists(LMaskFileName))
893  {
894  amrex::Print() << "Reading LMASK data" << std::endl;
895  int ntimes = 1;
896  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
897  MultiFab lmask_at_t(ba2d[lev],dmap[lev],1,ng);
898  for (int nt(0); nt<ntimes; ++nt) {
899  VisMF::Read(lmask_at_t, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_",
900  "LMASK_" + std::to_string(nt)));
901  for (MFIter mfi(lmask_at_t); mfi.isValid(); ++mfi) {
902  const Box& bx = mfi.growntilebox();
903  Array4<int> const& dst_arr = lmask_lev[lev][nt]->array(mfi);
904  Array4<Real> const& src_arr = lmask_at_t.array(mfi);
905  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
906  {
907  dst_arr(i,j,k) = int(src_arr(i,j,k));
908  });
909  }
910  }
911  } else {
912  lmask_lev[lev][0]->setVal(solverChoice.is_land[lev]);
913  lmask_lev[lev][0]->FillBoundary(geom[lev].periodicity());
914  }
915 
916  IntVect ngv = ng; ngv[2] = 0;
917 
918  // Read lat/lon if it exists
919  std::string LatFileName(restart_chkfile + "/Level_0/LAT_H");
920  if (amrex::FileExists(LatFileName)) {
921  amrex::Print() << "Reading Lat/Lon variables" << std::endl;
922  MultiFab lat(ba2d[lev],dmap[lev],1,ngv);
923  MultiFab lon(ba2d[lev],dmap[lev],1,ngv);
924  VisMF::Read(lat, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LAT"));
925  VisMF::Read(lon, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LON"));
926  lat_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
927  lon_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
928  MultiFab::Copy(*lat_m[lev],lat,0,0,1,ngv);
929  MultiFab::Copy(*lon_m[lev],lon,0,0,1,ngv);
930  }
931 
932 #ifdef ERF_USE_NETCDF
933  // Read sinPhi and cosPhi if it exists
934  std::string VarCorFileName(restart_chkfile + "/Level_0/SinPhi_H");
935  if (amrex::FileExists(VarCorFileName)) {
936  amrex::Print() << "Reading Coriolis factors" << std::endl;
937  MultiFab sphi(ba2d[lev],dmap[lev],1,ngv);
938  MultiFab cphi(ba2d[lev],dmap[lev],1,ngv);
939  VisMF::Read(sphi, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "SinPhi"));
940  VisMF::Read(cphi, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "CosPhi"));
941  sinPhi_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
942  cosPhi_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
943  MultiFab::Copy(*sinPhi_m[lev],sphi,0,0,1,ngv);
944  MultiFab::Copy(*cosPhi_m[lev],cphi,0,0,1,ngv);
945  }
946 
947  if (solverChoice.use_real_bcs && solverChoice.init_type == InitType::WRFInput) {
948  if (lev == 0) {
949  amrex::Print() << "Reading C1H/C2H/MUB/PHB variables at level " << lev << std::endl;
950  MultiFab tmp1d(ba1d[0],dmap[0],1,0);
951 
952  VisMF::Read(tmp1d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "C1H"));
953  MultiFab::Copy(*wrf_C1H,tmp1d,0,0,1,0);
954 
955  VisMF::Read(tmp1d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "C2H"));
956  MultiFab::Copy(*wrf_C2H,tmp1d,0,0,1,0);
957 
958  MultiFab tmp2d(ba2d[0],dmap[0],1,wrf_MUB->nGrowVect());
959 
960  VisMF::Read(tmp2d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MUB"));
961  MultiFab::Copy(*wrf_MUB,tmp2d,0,0,1,wrf_MUB->nGrowVect());
962 
963  ng = IntVect(1,1,0);
964  MultiFab tmp3d(convert(grids[0],IntVect(0,0,1)),dmap[0],1,ng);
965  wrf_PHB = std::make_unique<MultiFab>(convert(grids[0],IntVect(0,0,1)),dmap[0],1,ng);
966  VisMF::Read(tmp3d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "PHB"));
967  MultiFab::Copy(*wrf_PHB,tmp3d,0,0,1,ng);
968  }
969  }
970 #endif
971  } // for lev
972 
973  // Restore the LSM scalar step counter (e.g. NoahMP itimestep) so the
974  // substepping schedule survives restart. lsm.Init() during
975  // MakeNewLevelFromScratch has already reset itimestep to 0 (NoahmpIOVarInit);
976  // override it here from the checkpoint. Older checkpoints without this file
977  // restart with the legacy reset-to-zero behavior.
978  if (solverChoice.lsm_type != LandSurfaceType::None) {
979  std::string LsmStepFileName(restart_chkfile + "/lsm_step");
980  if (amrex::FileExists(LsmStepFileName)) {
981  Vector<char> LsmStepCharPtr;
982  ParallelDescriptor::ReadAndBcastFile(LsmStepFileName, LsmStepCharPtr);
983  std::string LsmStepStr(LsmStepCharPtr.dataPtr());
984  std::istringstream lsm_is(LsmStepStr, std::istringstream::in);
985  int step_val = 0;
986  for (int lev = 0; lev <= finest_level; ++lev) {
987  if (lsm_is >> step_val) {
988  lsm.Set_LSM_Step(lev, step_val);
989  amrex::Print() << "Restored LSM step counter at level " << lev
990  << " to " << step_val << std::endl;
991  }
992  }
993  } else {
994  amrex::Print() << "Warning: legacy checkpoint without lsm_step file; "
995  << "LSM substep schedule will reset (may break bitwise reproducibility)."
996  << std::endl;
997  }
998 
999  // Restore the full LSM prognostic state (e.g. NoahMP soil/snow/canopy)
1000  // from chk*/noahmp_restart. lsm.Init() during MakeNewLevelFromScratch
1001  // has already cold-initialized this state from wrfinput/tables; this
1002  // overwrites it with the checkpoint, and NoahMP's per-step In-transfer
1003  // pulls it into the physics state on the first Advance (issue #3255).
1004  // Legacy checkpoints without this directory fall back to cold-init.
1005  std::string LsmRestartDir(restart_chkfile + "/noahmp_restart");
1006  if (amrex::FileExists(LsmRestartDir + "/Level_0.nc")) {
1007  for (int lev = 0; lev <= finest_level; ++lev) {
1008  lsm.Read_Lsm_Restart(lev, LsmRestartDir);
1009  }
1010  amrex::Print() << "Restored full NoahMP prognostic state from "
1011  << LsmRestartDir << std::endl;
1012  } else {
1013  amrex::Print() << "Warning: legacy checkpoint without noahmp_restart; "
1014  << "NoahMP prognostic state cold-initialized from wrfinput "
1015  << "(land trajectory will differ from cold start)."
1016  << std::endl;
1017  }
1018  }
1019 
1020 #ifdef ERF_USE_PARTICLES
1021  restartTracers((ParGDBBase*)GetParGDB(),restart_chkfile);
1022  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
1023  dynamic_cast<LagrangianMicrophysics&>(*micro).restartParticles((ParGDBBase*)GetParGDB(),restart_chkfile);
1024  }
1025 #endif
1026 
1027 #if 0
1028 #ifdef ERF_USE_NETCDF
1029  // Read bdy_data files
1030  if ( ((solverChoice.init_type==InitType::WRFInput) || (solverChoice.init_type==InitType::Metgrid)) &&
1032  {
1033  int ioproc = ParallelDescriptor::IOProcessorNumber(); // I/O rank
1034  int num_time;
1035  int num_var;
1036  Vector<Box> bx_v;
1037  if (ParallelDescriptor::IOProcessor()) {
1038  // Open header file and read from it
1039  std::ifstream bdy_h_file(MultiFabFileFullPrefix(0, restart_chkfile, "Level_", "bdy_H"));
1040  bdy_h_file >> num_time;
1041  bdy_h_file >> num_var;
1042  bdy_h_file >> start_bdy_time;
1043  bdy_h_file >> bdy_time_interval;
1044  bdy_h_file >> real_width;
1045  bx_v.resize(4*num_var);
1046  for (int ivar(0); ivar<num_var; ++ivar) {
1047  bdy_h_file >> bx_v[4*ivar ];
1048  bdy_h_file >> bx_v[4*ivar+1];
1049  bdy_h_file >> bx_v[4*ivar+2];
1050  bdy_h_file >> bx_v[4*ivar+3];
1051  }
1052 
1053  // IO size the FABs
1054  bdy_data_xlo.resize(num_time);
1055  bdy_data_xhi.resize(num_time);
1056  bdy_data_ylo.resize(num_time);
1057  bdy_data_yhi.resize(num_time);
1058  for (int itime(0); itime<num_time; ++itime) {
1059  bdy_data_xlo[itime].resize(num_var);
1060  bdy_data_xhi[itime].resize(num_var);
1061  bdy_data_ylo[itime].resize(num_var);
1062  bdy_data_yhi[itime].resize(num_var);
1063  for (int ivar(0); ivar<num_var; ++ivar) {
1064  bdy_data_xlo[itime][ivar].resize(bx_v[4*ivar ]);
1065  bdy_data_xhi[itime][ivar].resize(bx_v[4*ivar+1]);
1066  bdy_data_ylo[itime][ivar].resize(bx_v[4*ivar+2]);
1067  bdy_data_yhi[itime][ivar].resize(bx_v[4*ivar+3]);
1068  }
1069  }
1070 
1071  // Open data file and read from it
1072  std::ifstream bdy_d_file(MultiFabFileFullPrefix(0, restart_chkfile, "Level_", "bdy_D"));
1073  for (int itime(0); itime<num_time; ++itime) {
1074  for (int ivar(0); ivar<num_var; ++ivar) {
1075  bdy_data_xlo[itime][ivar].readFrom(bdy_d_file);
1076  bdy_data_xhi[itime][ivar].readFrom(bdy_d_file);
1077  bdy_data_ylo[itime][ivar].readFrom(bdy_d_file);
1078  bdy_data_yhi[itime][ivar].readFrom(bdy_d_file);
1079  }
1080  }
1081  } // IO
1082 
1083  // Broadcast the data
1084  ParallelDescriptor::Barrier();
1085  ParallelDescriptor::Bcast(&start_bdy_time,1,ioproc);
1086  ParallelDescriptor::Bcast(&bdy_time_interval,1,ioproc);
1087  ParallelDescriptor::Bcast(&real_width,1,ioproc);
1088  ParallelDescriptor::Bcast(&num_time,1,ioproc);
1089  ParallelDescriptor::Bcast(&num_var,1,ioproc);
1090 
1091  // Everyone size their boxes
1092  bx_v.resize(4*num_var);
1093 
1094  ParallelDescriptor::Bcast(bx_v.dataPtr(),bx_v.size(),ioproc);
1095 
1096  // Everyone but IO size their FABs
1097  if (!ParallelDescriptor::IOProcessor()) {
1098  bdy_data_xlo.resize(num_time);
1099  bdy_data_xhi.resize(num_time);
1100  bdy_data_ylo.resize(num_time);
1101  bdy_data_yhi.resize(num_time);
1102  for (int itime(0); itime<num_time; ++itime) {
1103  bdy_data_xlo[itime].resize(num_var);
1104  bdy_data_xhi[itime].resize(num_var);
1105  bdy_data_ylo[itime].resize(num_var);
1106  bdy_data_yhi[itime].resize(num_var);
1107  for (int ivar(0); ivar<num_var; ++ivar) {
1108  bdy_data_xlo[itime][ivar].resize(bx_v[4*ivar ]);
1109  bdy_data_xhi[itime][ivar].resize(bx_v[4*ivar+1]);
1110  bdy_data_ylo[itime][ivar].resize(bx_v[4*ivar+2]);
1111  bdy_data_yhi[itime][ivar].resize(bx_v[4*ivar+3]);
1112  }
1113  }
1114  }
1115 
1116  for (int itime(0); itime<num_time; ++itime) {
1117  for (int ivar(0); ivar<num_var; ++ivar) {
1118  ParallelDescriptor::Bcast(bdy_data_xlo[itime][ivar].dataPtr(),bdy_data_xlo[itime][ivar].box().numPts(),ioproc);
1119  ParallelDescriptor::Bcast(bdy_data_xhi[itime][ivar].dataPtr(),bdy_data_xhi[itime][ivar].box().numPts(),ioproc);
1120  ParallelDescriptor::Bcast(bdy_data_ylo[itime][ivar].dataPtr(),bdy_data_ylo[itime][ivar].box().numPts(),ioproc);
1121  ParallelDescriptor::Bcast(bdy_data_yhi[itime][ivar].dataPtr(),bdy_data_yhi[itime][ivar].box().numPts(),ioproc);
1122  }
1123  }
1124  } // init_type == WRFInput or Metgrid
1125 #endif
1126 #endif
1127 
1128 #ifdef ERF_USE_NETCDF
1129  // Load boundary data from erfbdy during restart for metgrid or wrfinput.
1130  if (((solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid)) &&
1132 
1133  // Check for erfbdy file.
1134  std::string erfbdy_header = erfbdy_file + "/Header";
1135  use_erfbdy = FileSystem::Exists(erfbdy_header);
1136 
1137  if (solverChoice.init_type == InitType::Metgrid) {
1138  if (!use_erfbdy) {
1139  Abort("Restart with init_type=metgrid requires erfbdy file: " + erfbdy_file);
1140  }
1141  }
1142 
1143  // Load from erfbdy if it exists.
1144  if (use_erfbdy) {
1145  Print() << "Restart: Loading boundary data from erfbdy file: " << erfbdy_file << std::endl;
1146 
1147  int ntimes_erfbdy;
1148  Vector<Real> bdy_times;
1149  bdy_time_interval = read_times_from_erfbdy(erfbdy_file,
1150  ntimes_erfbdy, nvars_erfbdy, real_width,
1151  bdy_times, start_bdy_time, final_bdy_time);
1152 
1153  Print() << "Restart: erfbdy file contains " << ntimes_erfbdy << " times" << std::endl;
1154 
1155  bdy_data_xlo.resize(ntimes_erfbdy);
1156  bdy_data_xhi.resize(ntimes_erfbdy);
1157  bdy_data_ylo.resize(ntimes_erfbdy);
1158  bdy_data_yhi.resize(ntimes_erfbdy);
1159 
1160  // Determine which times we need based on current simulation time.
1161  Real time_since_start_bdy = t_new[0] + start_time - start_bdy_time;
1162  int n_time_old = std::min(static_cast<int>(time_since_start_bdy / bdy_time_interval), ntimes_erfbdy-1);
1163  int n_time_new = n_time_old + 1;
1164 
1165  // Read the necessary times into memory.
1166  for (int itime = n_time_old; itime <= std::min(n_time_new + 1, ntimes_erfbdy - 1); ++itime) {
1168  bdy_data_xlo, bdy_data_xhi,
1169  bdy_data_ylo, bdy_data_yhi,
1171  Print() << "Restart: Loaded erfbdy time index " << itime << std::endl;
1172  }
1173  }
1174  }
1175 #endif
1176 }
Real read_times_from_erfbdy(const std::string &bdy_file_name, int &ntimes, int &nvars, int &real_width, Vector< Real > &bdy_times, Real &start_bdy_time, Real &final_bdy_time)
Definition: ERF_ReadFromERFBdy.cpp:10
void read_from_erfbdy(int itime, const std::string &bdy_file_name, Vector< Vector< FArrayBox >> &bdy_data_xlo, Vector< Vector< FArrayBox >> &bdy_data_xhi, Vector< Vector< FArrayBox >> &bdy_data_ylo, Vector< Vector< FArrayBox >> &bdy_data_yhi, int nvars, int real_width)
Definition: ERF_ReadFromERFBdy.cpp:65
std::string erfbdy_file
Definition: ERF.H:1357
int nvars_erfbdy
Definition: ERF.H:1358
bool use_erfbdy
Definition: ERF.H:1356
static void GotoNextLine(std::istream &is)
Definition: ERF_Checkpoint.cpp:17
void MakeNewLevelFromScratch(int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
Definition: ERF_MakeNewLevel.cpp:25
void Read_Lsm_Restart(const int &lev, const std::string &dir)
Definition: ERF_LandSurface.H:135
void Set_LSM_Step(const int &lev, int step)
Definition: ERF_LandSurface.H:126
Here is the call graph for this function:

◆ ReadCheckpointFileSurfaceLayer()

void ERF::ReadCheckpointFileSurfaceLayer ( )

ERF function for reading additional data for MOST from a checkpoint file during restart.

This is called after the ABLMost object is instantiated.

1223 {
1224  for (int lev = 0; lev <= finest_level; ++lev)
1225  {
1226  amrex::Print() << "Reading MOST variables" << std::endl;
1227 
1228  IntVect ng(1,1,0);
1229 
1230  auto read_most_var = [&] (const std::string& name, MultiFab* dst) {
1231  const std::string mf_name = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", name);
1232  if (amrex::FileExists(mf_name + "_H")) {
1233  MultiFab m_var;
1234  VisMF::Read(m_var, mf_name);
1235  dst->ParallelCopy(m_var, 0, 0, 1, ng, ng, geom[lev].periodicity());
1236  }
1237  };
1238 
1239  // U*
1240  read_most_var("Ustar", m_SurfaceLayer->get_u_star(lev));
1241 
1242  // W*
1243  read_most_var("Wstar", m_SurfaceLayer->get_w_star(lev));
1244 
1245  // T*
1246  read_most_var("Tstar", m_SurfaceLayer->get_t_star(lev));
1247 
1248  // Q*
1249  read_most_var("Qstar", m_SurfaceLayer->get_q_star(lev));
1250 
1251  // Olen
1252  read_most_var("Olen", m_SurfaceLayer->get_olen(lev));
1253 
1254  // Qsurf
1255  read_most_var("Qsurf", m_SurfaceLayer->get_q_surf(lev));
1256 
1257  // PBLH
1258  read_most_var("PBLH", m_SurfaceLayer->get_pblh(lev));
1259 
1260  // Z0
1261  read_most_var("Z0", m_SurfaceLayer->get_z0(lev));
1262  }
1263 }

◆ ReadParameters()

void ERF::ReadParameters ( )
private
1951 {
1952  std::string prob_name = "Undefined";
1953  ParmParse pp_pn("erf");
1954  pp_pn.queryAdd("prob_name", prob_name);
1955  Print() << "Problem name (from inputs file) is: "
1956  << " \"" << prob_name << "\" " << std::endl;
1957 
1958  ParmParse pp(pp_prefix);
1959  ParmParse pp_amr("amr");
1960  {
1961  pp.query("regrid_level_0_on_restart", regrid_level_0_on_restart);
1962  pp.query("regrid_int", regrid_int);
1963  pp.query("check_file", check_file);
1964 
1965  // The regression tests use "amr.restart" and "amr.m_check_int" so we allow
1966  // for those or "erf.restart" / "erf.m_check_int" with the former taking
1967  // precedence if both are specified
1968  pp.query("check_int", m_check_int);
1969  pp.query("check_per", m_check_per);
1970  pp_amr.query("check_int", m_check_int);
1971  pp_amr.query("check_per", m_check_per);
1972 
1973  pp.query("restart", restart_chkfile);
1974  pp_amr.query("restart", restart_chkfile);
1975 
1976  // Verbosity
1977  pp.query("v", verbose);
1978  pp.query("mg_v", mg_verbose);
1979  pp.query("use_fft", use_fft);
1980 #ifndef ERF_USE_FFT
1981  if (use_fft) {
1982  Abort("You must build with USE_FFT in order to set use_fft = true in your inputs file");
1983  }
1984 #endif
1985 
1986  // Check for NaNs?
1987  pp.query("check_for_nans", check_for_nans);
1988 
1989  // Frequency of diagnostic output
1990  pp.query("sum_interval", sum_interval);
1991  pp.query("sum_period" , sum_per);
1992 
1993  pp.query("pert_interval", pert_interval);
1994 
1995  // Time step controls
1996  pp.query("cfl", cfl);
1997  pp.query("substepping_cfl", sub_cfl);
1998  pp.query("init_shrink", init_shrink);
1999  pp.query("change_max", change_max);
2000  pp.query("dt_max_initial", dt_max_initial);
2001  pp.query("dt_max", dt_max);
2002 
2003  fixed_dt.resize(max_level+1,-one);
2004  fixed_fast_dt.resize(max_level+1,-one);
2005 
2006  pp.query("fixed_dt", fixed_dt[0]);
2007  pp.query("fixed_fast_dt", fixed_fast_dt[0]);
2008 
2009  int nlevs_max = max_level + 1;
2010  istep.resize(nlevs_max, 0);
2011  nsubsteps.resize(nlevs_max, 1);
2012  // This is the default
2013  for (int lev = 1; lev <= max_level; ++lev) {
2014  nsubsteps[lev] = MaxRefRatio(lev-1);
2015  }
2016 
2017  if (max_level > 0) {
2018  ParmParse pp_erf("erf");
2019  int count = pp_erf.countval("dt_ref_ratio");
2020  if (count > 0) {
2021  Vector<int> nsub;
2022  nsub.resize(nlevs_max, 0);
2023  if (count == 1) {
2024  pp_erf.queryarr("dt_ref_ratio", nsub, 0, 1);
2025  for (int lev = 1; lev <= max_level; ++lev) {
2026  nsubsteps[lev] = nsub[0];
2027  }
2028  } else {
2029  pp_erf.queryarr("dt_ref_ratio", nsub, 0, max_level);
2030  for (int lev = 1; lev <= max_level; ++lev) {
2031  nsubsteps[lev] = nsub[lev-1];
2032  }
2033  }
2034  }
2035  }
2036 
2037  // Make sure we do this after we have defined nsubsteps above
2038  for (int lev = 1; lev <= max_level; lev++)
2039  {
2040  fixed_dt[lev] = fixed_dt[lev-1] / static_cast<Real>(nsubsteps[lev]);
2041  fixed_fast_dt[lev] = fixed_fast_dt[lev-1] / static_cast<Real>(nsubsteps[lev]);
2042  }
2043 
2044  pp.query("fixed_mri_dt_ratio", fixed_mri_dt_ratio);
2045 
2046  // We use this to keep track of how many boxes we read in from WRF initialization
2047  num_files_at_level.resize(max_level+1,0);
2048 
2049  // We use this to keep track of how many boxes are specified thru the refinement indicators
2050  num_boxes_at_level.resize(max_level+1,0);
2051  boxes_at_level.resize(max_level+1);
2052 
2053  // We always have exactly one file at level 0
2054  num_boxes_at_level[0] = 1;
2055  boxes_at_level[0].resize(1);
2056  boxes_at_level[0][0] = geom[0].Domain();
2057 
2058 #ifdef ERF_USE_NETCDF
2059  nc_init_file.resize(max_level+1);
2060  have_read_nc_init_file.resize(max_level+1);
2061 
2062  // NetCDF wrfinput initialization files -- possibly multiple files at each of multiple levels
2063  // but we always have exactly one file at level 0
2064  for (int lev = 0; lev <= max_level; lev++) {
2065  const std::string nc_file_names = Concatenate("nc_init_file_",lev,1);
2066  if (pp.contains(nc_file_names.c_str())) {
2067  int num_files = pp.countval(nc_file_names.c_str());
2068  num_files_at_level[lev] = num_files;
2069  nc_init_file[lev].resize(num_files);
2070  have_read_nc_init_file[lev].resize(num_files);
2071  pp.queryarr(nc_file_names.c_str(), nc_init_file[lev],0,num_files);
2072  for (int j = 0; j < num_files; j++) {
2073  Print() << "Reading NC init file names at level " << lev << " and index " << j << " : " << nc_init_file[lev][j] << std::endl;
2074  have_read_nc_init_file[lev][j] = 0;
2075  } // j
2076  } // if pp.contains
2077  } // lev
2078 
2079  // NetCDF wrfbdy lateral boundary file
2080  if (pp.query("nc_bdy_file", nc_bdy_file)) {
2081  Print() << "Reading NC bdy file name " << nc_bdy_file << std::endl;
2082  }
2083 
2084  // NetCDF wrflow lateral boundary file
2085  if (pp.query("nc_low_file", nc_low_file)) {
2086  Print() << "Reading NC low file name " << nc_low_file << std::endl;
2087  }
2088 
2089 #endif
2090 
2091  // Options for vertical interpolation of met_em*.nc data.
2092  pp.query("metgrid_debug_quiescent", metgrid_debug_quiescent);
2093  pp.query("metgrid_debug_isothermal", metgrid_debug_isothermal);
2094  pp.query("metgrid_debug_dry", metgrid_debug_dry);
2095  pp.query("metgrid_debug_psfc", metgrid_debug_psfc);
2096  pp.query("metgrid_debug_msf", metgrid_debug_msf);
2097  pp.query("metgrid_interp_theta", metgrid_interp_theta);
2098  pp.query("metgrid_basic_linear", metgrid_basic_linear);
2099  pp.query("metgrid_use_below_sfc", metgrid_use_below_sfc);
2100  pp.query("metgrid_use_sfc", metgrid_use_sfc);
2101  pp.query("metgrid_retain_sfc", metgrid_retain_sfc);
2102  pp.query("metgrid_proximity", metgrid_proximity);
2103  pp.query("metgrid_order", metgrid_order);
2104  pp.query("metgrid_force_sfc_k", metgrid_force_sfc_k);
2105 
2106  // Options for boundary file.
2107  pp.query("write_erfbdy", write_erfbdy);
2108  pp.query("erfbdy_file", erfbdy_file);
2109 
2110  // Set default to FullState for now ... later we will try Perturbation
2111  interpolation_type = StateInterpType::FullState;
2112  pp.query_enum_case_insensitive("interpolation_type" ,interpolation_type);
2113 
2114  PlotFileType plotfile3d_type_temp = PlotFileType::None;
2115  pp.query_enum_case_insensitive("plotfile_type" ,plotfile3d_type_temp);
2116  pp.query_enum_case_insensitive("plotfile_type_1",plotfile3d_type_1);
2117  pp.query_enum_case_insensitive("plotfile_type_2",plotfile3d_type_2);
2118 
2119  PlotFileType plotfile2d_type_temp = PlotFileType::None;
2120  pp.query_enum_case_insensitive("plotfile2d_type" ,plotfile2d_type_temp);
2121  pp.query_enum_case_insensitive("plotfile2d_type_1",plotfile2d_type_1);
2122  pp.query_enum_case_insensitive("plotfile2d_type_2",plotfile2d_type_2);
2123  //
2124  // This option is for backward consistency -- if only plotfile_type is set,
2125  // then it will be used for both 1 and 2 if and only if they are not set
2126  //
2127  // Default is native amrex if no type is specified
2128  //
2129  if (plotfile3d_type_temp == PlotFileType::None) {
2130  if (plotfile3d_type_1 == PlotFileType::None) {
2131  plotfile3d_type_1 = PlotFileType::Amrex;
2132  }
2133  if (plotfile3d_type_2 == PlotFileType::None) {
2134  plotfile3d_type_2 = PlotFileType::Amrex;
2135  }
2136  } else {
2137  if (plotfile3d_type_1 == PlotFileType::None) {
2138  plotfile3d_type_1 = plotfile3d_type_temp;
2139  } else {
2140  Abort("You must set either plotfile_type or plotfile_type_1, not both");
2141  }
2142  if (plotfile3d_type_2 == PlotFileType::None) {
2143  plotfile3d_type_2 = plotfile3d_type_temp;
2144  } else {
2145  Abort("You must set either plotfile_type or plotfile_type_2, not both");
2146  }
2147  }
2148  if (plotfile2d_type_temp == PlotFileType::None) {
2149  if (plotfile2d_type_1 == PlotFileType::None) {
2150  plotfile2d_type_1 = PlotFileType::Amrex;
2151  }
2152  if (plotfile2d_type_2 == PlotFileType::None) {
2153  plotfile2d_type_2 = PlotFileType::Amrex;
2154  }
2155  } else {
2156  if (plotfile2d_type_1 == PlotFileType::None) {
2157  plotfile2d_type_1 = plotfile2d_type_temp;
2158  } else {
2159  Abort("You must set either plotfile2d_type or plotfile2d_type_1, not both");
2160  }
2161  if (plotfile2d_type_2 == PlotFileType::None) {
2162  plotfile2d_type_2 = plotfile2d_type_temp;
2163  } else {
2164  Abort("You must set either plotfile2d_type or plotfile2d_type_2, not both");
2165  }
2166  }
2167 #ifndef ERF_USE_NETCDF
2168  if (plotfile3d_type_1 == PlotFileType::Netcdf ||
2169  plotfile3d_type_2 == PlotFileType::Netcdf ||
2170  plotfile2d_type_1 == PlotFileType::Netcdf ||
2171  plotfile2d_type_2 == PlotFileType::Netcdf) {
2172  Abort("Plotfile type = Netcdf is not allowed without USE_NETCDF = TRUE");
2173  }
2174 #endif
2175 
2176  pp.query("plot_file_1" , plot3d_file_1);
2177  pp.query("plot_file_2" , plot3d_file_2);
2178  pp.query("plot2d_file_1", plot2d_file_1);
2179  pp.query("plot2d_file_2", plot2d_file_2);
2180 
2181  pp.query("plot_int_1" , m_plot3d_int_1);
2182  pp.query("plot_int_2" , m_plot3d_int_2);
2183  pp.query("plot_per_1" , m_plot3d_per_1);
2184  pp.query("plot_per_2" , m_plot3d_per_2);
2185 
2186  pp.query("plot2d_int_1" , m_plot2d_int_1);
2187  pp.query("plot2d_int_2" , m_plot2d_int_2);
2188  pp.query("plot2d_per_1", m_plot2d_per_1);
2189  pp.query("plot2d_per_2", m_plot2d_per_2);
2190 
2191  pp.query("subvol_file", subvol_file);
2192 
2193  // Should we use format like plt1970-01-01_00:00:Real(00.000000) (if true) or plt00001 (if false)
2194  pp.query("use_real_time_in_pltname", use_real_time_in_pltname);
2195 
2196  // If use_real_time_in_pltname is false, how many digits should we use for the timestep?
2197  pp.query("file_name_digits", file_name_digits);
2198 
2199  // Default if subvol_int not specified
2200  m_subvol_int.resize(1); m_subvol_int[0] = -1;
2201  m_subvol_per.resize(1); m_subvol_per[0] = -one;
2202  last_subvol_step.resize(1);
2203  last_subvol_time.resize(1);
2204 
2205  int nsi = pp.countval("subvol_int");
2206  int nsr = pp.countval("subvol_per");
2207 
2208  // We must specify only subvol_int OR subvol_per
2209  AMREX_ALWAYS_ASSERT (!(nsi > 0 && nsr > 0));
2210 
2211  int nsub = -1;
2212  if (nsi > 0 || nsr > 0) {
2213  ParmParse pp_sv("erf.subvol");
2214  int n1 = pp_sv.countval("origin"); int n2 = pp_sv.countval("nxnynz"); int n3 = pp_sv.countval("dxdydz");
2215  if (n1 != n2 || n1 != n3 || n2 != n3) {
2216  Abort("WriteSubvolume: must have same number of entries in origin, nxnynz, and dxdydz.");
2217  }
2218  if ( n1%AMREX_SPACEDIM != 0) {
2219  Abort("WriteSubvolume: origin, nxnynz, and dxdydz must have multiples of AMReX_SPACEDIM");
2220  }
2221  nsub = n1/AMREX_SPACEDIM;
2222  m_subvol_int.resize(nsub);
2223  last_subvol_step.resize(nsub);
2224  last_subvol_time.resize(nsub);
2225  m_subvol_int.resize(nsub);
2226  m_subvol_per.resize(nsub);
2227  }
2228 
2229  if (nsi > 0) {
2230  for (int i = 1; i < nsub; i++) m_subvol_per[i] = -one;
2231  if ( nsi == 1) {
2232  m_subvol_int[0] = -1;
2233  pp.get("subvol_int" , m_subvol_int[0]);
2234  } else if ( nsi == nsub) {
2235  pp.getarr("subvol_int" , m_subvol_int);
2236  } else {
2237  Abort("There must either be a single value of subvol_int or one for every subdomain");
2238  }
2239  }
2240 
2241  if (nsr > 0) {
2242  for (int i = 1; i < nsub; i++) m_subvol_int[i] = -static_cast<int>(one);
2243  if ( nsr == 1) {
2244  m_subvol_per[0] = -one;
2245  pp.get("subvol_per" , m_subvol_per[0]);
2246  } else if ( nsr == nsub) {
2247  pp.getarr("subvol_per" , m_subvol_per);
2248  } else {
2249  Abort("There must either be a single value of subvol_per or one for every subdomain");
2250  }
2251  }
2252 
2253  setSubVolVariables("subvol_sampling_vars",subvol3d_var_names);
2254 
2255  pp.query("expand_plotvars_to_unif_rr",m_expand_plotvars_to_unif_rr);
2256 
2257  pp.query("plot_face_vels",m_plot_face_vels);
2258 
2259  if ( (m_plot3d_int_1 > 0 && m_plot3d_per_1 > 0) ||
2260  (m_plot3d_int_2 > 0 && m_plot3d_per_2 > zero) ) {
2261  Abort("Must choose only one of plot_int or plot_per");
2262  }
2263  if ( (m_plot2d_int_1 > 0 && m_plot2d_per_1 > 0) ||
2264  (m_plot2d_int_2 > 0 && m_plot2d_per_2 > zero) ) {
2265  Abort("Must choose only one of plot_int or plot_per");
2266  }
2267 
2268  pp.query("profile_int", profile_int);
2269  pp.query("destag_profiles", destag_profiles);
2270 
2271  pp.query("plot_lsm", plot_lsm);
2272 #ifdef ERF_USE_RRTMGP
2273  pp.query("plot_rad", plot_rad);
2274 #endif
2275  pp.query("profile_rad_int", rad_datalog_int);
2276 
2277  pp.query("output_1d_column", output_1d_column);
2278  pp.query("column_per", column_per);
2279  pp.query("column_interval", column_interval);
2280  pp.query("column_loc_x", column_loc_x);
2281  pp.query("column_loc_y", column_loc_y);
2282  pp.query("column_file_name", column_file_name);
2283 
2284  // Sampler output frequency
2285  pp.query("line_sampling_per", line_sampling_per);
2286  pp.query("line_sampling_interval", line_sampling_interval);
2287  pp.query("plane_sampling_per", plane_sampling_per);
2288  pp.query("plane_sampling_interval", plane_sampling_interval);
2289 
2290  // Specify information about outputting planes of data
2291  pp.query("output_bndry_planes", output_bndry_planes);
2292  pp.query("bndry_output_planes_interval", bndry_output_planes_interval);
2293  pp.query("bndry_output_planes_per", bndry_output_planes_per);
2294  pp.query("bndry_output_start_time", bndry_output_planes_start_time);
2295 
2296  // Specify whether ingest boundary planes of data
2297  pp.query("input_bndry_planes", input_bndry_planes);
2298 
2299  // Query the total width for wrfbdy interior ghost cells
2300  pp.query("real_width", real_width);
2301 
2302  // If using real boundaries, do we extrapolate w (or set to 0)
2303  pp.query("real_extrap_w", real_extrap_w);
2304 
2305  // Query the set and total widths for crse-fine interior ghost cells
2306  pp.query("cf_width", cf_width);
2307  pp.query("cf_set_width", cf_set_width);
2308 
2309  // AmrMesh iterate on grids?
2310  bool iterate(true);
2311  pp_amr.query("iterate_grids",iterate);
2312  if (!iterate) SetIterateToFalse();
2313  }
2314 
2315 #ifdef ERF_USE_PARTICLES
2316  readTracersParams();
2317 #endif
2318 
2319  solverChoice.init_params(max_level,pp_prefix);
2320 
2321  // Set a default value for write_erfbdy following these rules.
2322  // Prioritize write_erfbdy provided by user.
2323  // write_erfbdy must be false for restarts.
2324  // write_erfbdy defaults to true for clean starts of the metgrid or wrfinput pathways.
2325  {
2326  ParmParse pp(pp_prefix);
2327  bool is_restart = !restart_chkfile.empty();
2328  if (is_restart) {
2329  if (write_erfbdy) {
2330  Abort("Cannot set erf.write_erfbdy = true during restart. erfbdy should only be written during initial runs.");
2331  }
2332  } else {
2333  if (!pp.contains("write_erfbdy")) {
2334  if ((solverChoice.init_type == InitType::Metgrid) || (solverChoice.init_type == InitType::WRFInput)) {
2335  write_erfbdy = true;
2336  }
2337  }
2338  }
2339  }
2340 
2341  {
2342  ParmParse pp_no_prefix; // Traditionally, max_step and stop_time do not have prefix.
2343  pp_no_prefix.query("max_step", max_step);
2344  if (max_step < 0) {
2345  max_step = std::numeric_limits<int>::max();
2346  }
2347 
2348  std::string start_datetime, stop_datetime;
2349  if (pp_no_prefix.query("start_datetime", start_datetime)) {
2350  if (start_datetime.length() == 16) { // YYYY-MM-DD HH:MM
2351  start_datetime += ":00"; // add seconds
2352  }
2353  if (start_datetime.length() != 19) {
2354  Print() << "Got start_datetime = \"" << start_datetime
2355  << "\", format should be " << datetime_format << std::endl;
2356  exit(0);
2357  }
2358  start_time = static_cast<amrex::Real>(getEpochTime(start_datetime, datetime_format));
2359 
2360 #ifdef ERF_USE_NETCDF
2361  if (solverChoice.init_type == InitType::WRFInput) {
2362  // This is the start time as written in the wrfinput file
2363  Real start_time_from_wrfinput = read_start_time_from_wrfinput(0, nc_init_file[0][0]);
2364  if (start_time != start_time_from_wrfinput) {
2365  amrex::Print() << "start_datetime from inputs file = " << start_time <<
2366  " does not match SIMULATION START DATE from wrfinput = " <<
2367  start_time_from_wrfinput << std::endl;
2368  amrex::Abort();
2369  }
2370  } else if (solverChoice.init_type == InitType::Metgrid) {
2371  // This is the start time as written in the metgrid file
2372  Real start_time_from_metgrid = read_start_time_from_metgrid(0, nc_init_file[0][0]);
2373  if (start_time != start_time_from_metgrid) {
2374  amrex::Print() << "start_datetime from inputs file = " << start_time <<
2375  " does not match SIMULATION START DATE from metgrid = " <<
2376  start_time_from_metgrid << std::endl;
2377  amrex::Abort();
2378  }
2379  }
2380 #endif
2381  Print() << "Start datetime : " << start_datetime << std::endl;
2382 
2383  use_datetime = true;
2384 
2385  } else {
2386 
2387 #ifdef ERF_USE_NETCDF
2388  if (solverChoice.init_type == InitType::WRFInput) {
2389  // This is the start time as written in the wrfinput file
2390  Real start_time_from_wrfinput = read_start_time_from_wrfinput(0, nc_init_file[0][0]);
2391  start_time = start_time_from_wrfinput;
2392 
2393  use_datetime = true;
2394 
2395  if (pp_no_prefix.query("start_time", start_time)) {
2396  amrex::Print() << "start_time should not be set from inputs file; we are reading SIMULATION START DATE from wrfinput" << std::endl;
2397  amrex::Abort();
2398  }
2399  } else if (solverChoice.init_type == InitType::Metgrid) {
2400  // This is the start time as written in the metgrid file
2401  Real start_time_from_metgrid = read_start_time_from_metgrid(0, nc_init_file[0][0]);
2402  start_time = start_time_from_metgrid;
2403 
2404  use_datetime = true;
2405 
2406  if (pp_no_prefix.query("start_time", start_time)) {
2407  amrex::Print() << "start_time should not be set from inputs file; we are reading SIMULATION START DATE from metgrid" << std::endl;
2408  amrex::Abort();
2409  }
2410  }
2411 #endif
2412  }
2413 
2414  if (pp_no_prefix.query("stop_datetime", stop_datetime)) {
2415  if (stop_datetime.length() == 16) { // YYYY-MM-DD HH:MM
2416  stop_datetime += ":00"; // add seconds
2417  }
2418  if (stop_datetime.length() != 19) {
2419  Print() << "Got stop_datetime = \"" << stop_datetime
2420  << "\", format should be " << datetime_format << std::endl;
2421  exit(0);
2422  }
2423 
2424  stop_time = static_cast<amrex::Real>(getEpochTime(stop_datetime, datetime_format));
2425  Print() << "Stop datetime : " << start_datetime << std::endl;
2426 
2427  } else {
2428 
2429  if (pp_no_prefix.query("stop_time", stop_time)) {
2430  Print() << "Maximum simulation length based on stop_time: " << stop_time << " s (elapsed) " << std::endl;
2431  amrex::Print() <<" Adding stop time " << stop_time << " to start_time " << start_time << std::endl;
2432  stop_time += start_time;
2433  }
2434  }
2435  }
2436 
2437 #ifndef ERF_USE_NETCDF
2438  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(( (solverChoice.init_type != InitType::WRFInput) &&
2439  (solverChoice.init_type != InitType::Metgrid ) &&
2440  (solverChoice.init_type != InitType::NCFile ) ),
2441  "init_type cannot be 'WRFInput', 'Metgrid' or 'NCFile' if we don't build with netcdf!");
2442 #endif
2443 
2444  // Query the canopy model file name
2445  std::string forestfile;
2446  solverChoice.do_forest_drag = pp.query("forest_file", forestfile);
2448  for (int lev = 0; lev <= max_level; ++lev) {
2449  m_forest_drag[lev] = std::make_unique<ForestDrag>(forestfile);
2450  }
2451  }
2452 
2453  // If init from WRFInput or Metgrid make sure a valid file name is present at level zero
2454  // We allow for the possibility that finer levels may use native refinement rather than reading from a file
2455  if ((solverChoice.init_type == InitType::WRFInput) ||
2456  (solverChoice.init_type == InitType::Metgrid) ||
2457  (solverChoice.init_type == InitType::NCFile) ) {
2458  int num_files = static_cast<int>(nc_init_file[0].size());
2459  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(num_files>0, "A file name must be present at level 0 for init type WRFInput, Metgrid or NCFile.");
2460  for (int j = 0; j < num_files; j++) {
2461  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!nc_init_file[0][j].empty(), "Valid file name must be present at level 0 for init type WRFInput, Metgrid or NCFile.");
2462  } //j
2463  } // InitType
2464 
2465  // What type of land surface model to use
2466  // NOTE: Must be checked after init_params
2467  if (solverChoice.lsm_type == LandSurfaceType::SLM) {
2468  lsm.SetModel<SLM>();
2469  Print() << "SLM land surface model!\n";
2470  } else if (solverChoice.lsm_type == LandSurfaceType::MM5) {
2471  lsm.SetModel<MM5>();
2472  Print() << "MM5 land surface model!\n";
2473 #ifdef ERF_USE_NOAHMP
2474  } else if (solverChoice.lsm_type == LandSurfaceType::NOAHMP) {
2475  lsm.SetModel<NOAHMP>();
2476  Print() << "Noah-MP land surface model!\n";
2477 #endif
2478  } else if (solverChoice.lsm_type == LandSurfaceType::OceanSurf) {
2479  lsm.SetModel<OceanSurf>();
2480  Print() << "OceanSurf land surface model!\n";
2481  } else if (solverChoice.lsm_type == LandSurfaceType::None) {
2482  lsm.SetModel<NullSurf>();
2483  Print() << "Null land surface model!\n";
2484  } else {
2485  Abort("Dont know this LandSurfaceType!") ;
2486  }
2487 
2488  if (verbose > 0) {
2489  solverChoice.display(max_level,pp_prefix);
2490  }
2491 
2493 }
AMREX_GPU_HOST AMREX_FORCE_INLINE std::time_t getEpochTime(const std::string &dateTime, const std::string &dateTimeFormat)
Definition: ERF_EpochTime.H:34
bool metgrid_basic_linear
Definition: ERF.H:1346
bool metgrid_debug_msf
Definition: ERF.H:1344
std::string plot2d_file_2
Definition: ERF.H:1173
std::string plot3d_file_1
Definition: ERF.H:1170
bool plot_rad
Definition: ERF.H:986
bool m_plot_face_vels
Definition: ERF.H:1188
std::string plot3d_file_2
Definition: ERF.H:1171
int regrid_int
Definition: ERF.H:1163
bool write_erfbdy
Definition: ERF.H:1355
bool metgrid_retain_sfc
Definition: ERF.H:1349
int file_name_digits
Definition: ERF.H:1322
bool metgrid_use_sfc
Definition: ERF.H:1348
bool metgrid_debug_quiescent
Definition: ERF.H:1340
bool metgrid_interp_theta
Definition: ERF.H:1345
bool regrid_level_0_on_restart
Definition: ERF.H:1167
int metgrid_force_sfc_k
Definition: ERF.H:1352
void setSubVolVariables(const std::string &pp_subvol_var_names, amrex::Vector< std::string > &subvol_var_names)
Definition: ERF_WriteSubvolume.cpp:9
bool real_extrap_w
Definition: ERF.H:1334
bool metgrid_use_below_sfc
Definition: ERF.H:1347
std::string subvol_file
Definition: ERF.H:1174
amrex::Real metgrid_proximity
Definition: ERF.H:1350
std::string plot2d_file_1
Definition: ERF.H:1172
bool metgrid_debug_dry
Definition: ERF.H:1342
bool metgrid_debug_isothermal
Definition: ERF.H:1341
bool use_real_time_in_pltname
Definition: ERF.H:1323
bool metgrid_debug_psfc
Definition: ERF.H:1343
void ParameterSanityChecks()
Definition: ERF.cpp:2497
bool m_expand_plotvars_to_unif_rr
Definition: ERF.H:1175
std::string check_file
Definition: ERF.H:1197
int metgrid_order
Definition: ERF.H:1351
bool plot_lsm
Definition: ERF.H:1190
void SetModel()
Definition: ERF_LandSurface.H:29
Definition: ERF_MM5.H:26
Definition: ERF_NOAHMP.H:85
Definition: ERF_NullSurf.H:8
Definition: ERF_OceanSurf.H:19
Definition: ERF_SLM.H:26
void display(int max_level, std::string pp_prefix)
Definition: ERF_DataStruct.H:994
void init_params(int max_level, std::string pp_prefix)
Definition: ERF_DataStruct.H:143

Referenced by ERF_shared().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadStormTrackerRestart()

void ERF::ReadStormTrackerRestart ( )
269 {
270  hurricane_eye_track_xy.clear();
272 
273  const fs::path base_dir("Output_StormTracker");
274 
275  // Nothing to do for a fresh run.
276  if (!fs::exists(base_dir)) {
277  return;
278  }
279  //
280  // Return the alphabetically last file in a directory.
281  // Since the filenames are zero-padded, this is also the newest output.
282  //
283  auto last_file = [](const fs::path& dir) -> fs::path
284  {
285  std::vector<fs::path> files;
286 
287  for (const auto& entry : fs::directory_iterator(dir)) {
288  if (entry.is_regular_file()) {
289  files.push_back(entry.path());
290  }
291  }
292 
293  if (files.empty()) {
294  return fs::path{};
295  }
296 
297  std::sort(files.begin(), files.end());
298 
299  return files.back();
300  };
301 
302  //==========================================================
303  // Read lat/lon file
304  //==========================================================
305 
306  {
307  fs::path file = last_file(base_dir / "latlon");
308 
309  if (!file.empty())
310  {
311  std::ifstream ifs(file);
312 
313  if (!ifs.is_open()) {
314  Abort("Could not open " + file.string());
315  }
316 
317  std::string line;
318 
319  // Skip the header line.
320  std::getline(ifs, line);
321 
322  amrex::Real lat, lon;
323 
324  while (ifs >> lat >> lon)
325  {
326  hurricane_eye_track_latlon.push_back({lat, lon});
327  }
328  }
329  }
330 
331  //==========================================================
332  // Read XY VTK file
333  //==========================================================
334 
335  {
336  fs::path file = last_file(base_dir / "xy");
337 
338  if (!file.empty())
339  {
340  std::ifstream ifs(file);
341  std::string line;
342 
343  // Skip the first four header lines.
344  for (int i = 0; i < 4; ++i) {
345  std::getline(ifs, line);
346  }
347 
348 
349  std::getline(ifs, line);
350  std::istringstream iss(line);
351  std::string keyword, datatype;
352  int npoints;
353 
354  iss >> keyword >> npoints >> datatype;
355  hurricane_eye_track_xy.reserve(npoints);
356 
357  for (int i = 0; i < npoints; ++i)
358  {
359  amrex::Real x, y, z;
360  ifs >> x >> y >> z;
361  hurricane_eye_track_xy.push_back({x, y});
362  }
363  }
364  }
365 }

◆ ReadVelsOnlyFromCheckpointFile()

void ERF::ReadVelsOnlyFromCheckpointFile ( int  lev_to_fill,
std::string &  chkfile_for_vels 
)

ERF function for reading data from a checkpoint file during restart.

1183 {
1184  Print() << "Reading vels only from native checkpoint " << chkfile_for_vels << " at level " << lev_to_fill << "\n";
1185 
1186  // Header
1187  std::string File(chkfile_for_vels + "/Header");
1188 
1189  VisMF::IO_Buffer io_buffer(VisMF::GetIOBufferSize());
1190 
1191  Vector<char> fileCharPtr;
1192  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
1193  std::string fileCharPtrString(fileCharPtr.dataPtr());
1194  std::istringstream is(fileCharPtrString, std::istringstream::in);
1195 
1196  AMREX_ALWAYS_ASSERT(lev_to_fill >= 0 && lev_to_fill <= finest_level);
1197 
1198  int lev = lev_to_fill;
1199 
1200  MultiFab xvel(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
1201  VisMF::Read(xvel, MultiFabFileFullPrefix(lev, chkfile_for_vels, "Level_", "XFace"));
1202  MultiFab::Copy(vars_new[lev][Vars::xvel],xvel,0,0,1,0);
1203  vars_new[lev][Vars::xvel].setBndry(bogus_large_value);
1204 
1205  MultiFab yvel(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
1206  VisMF::Read(yvel, MultiFabFileFullPrefix(lev, chkfile_for_vels, "Level_", "YFace"));
1207  MultiFab::Copy(vars_new[lev][Vars::yvel],yvel,0,0,1,0);
1208  vars_new[lev][Vars::yvel].setBndry(bogus_large_value);
1209 
1210  MultiFab zvel(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
1211  VisMF::Read(zvel, MultiFabFileFullPrefix(lev, chkfile_for_vels, "Level_", "ZFace"));
1212  MultiFab::Copy(vars_new[lev][Vars::zvel],zvel,0,0,1,0);
1213  vars_new[lev][Vars::zvel].setBndry(bogus_large_value);
1214 }
Here is the call graph for this function:

◆ refinement_criteria_setup()

void ERF::refinement_criteria_setup ( )
private

Function to define the refinement criteria based on user input

473 {
474  if (max_level > 0)
475  {
476  ParmParse pp(pp_prefix);
477  Vector<std::string> refinement_indicators;
478  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
479 
480  for (int i=0; i<refinement_indicators.size(); ++i)
481  {
482  std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
483 
484  ParmParse ppr(ref_prefix);
485  RealBox real_box;
486  int lev_for_box;
487 
488  read_box_for_refinement(ref_prefix, lev_for_box, real_box);
489 
490  AMRErrorTagInfo info;
491 
492  if (real_box.ok()) {
493  info.SetRealBox(real_box);
494  }
495 
496  if (ppr.countval("start_time") > 0) {
497  Real ref_min_time; ppr.get("start_time",ref_min_time);
498  info.SetMinTime(ref_min_time);
499  }
500 
501  if (ppr.countval("end_time") > 0) {
502  Real ref_max_time; ppr.get("end_time",ref_max_time);
503  info.SetMaxTime(ref_max_time);
504  }
505 
506  if (ppr.countval("max_level") > 0) {
507  int ref_max_level; ppr.get("max_level",ref_max_level);
508  info.SetMaxLevel(ref_max_level);
509  }
510 
511  // Read field_name once and validate moisture-field requests against
512  // the active moisture model so an unsupported field aborts at setup
513  // rather than after the first regrid.
514  std::string field;
515  if (ppr.countval("field_name") > 0) {
516  ppr.get("field_name", field);
517  auto is_moist_field = [](const std::string& f) {
518  return f == "qv" || f == "qc" || f == "qi" ||
519  f == "qr" || f == "qs" || f == "qg" || f == "qt";
520  };
521  if (is_moist_field(field)) {
522  const auto& mi = solverChoice.moisture_indices;
523  int comp = -1;
524  if (field == "qv") { comp = mi.qv; }
525  else if (field == "qc") { comp = mi.qc; }
526  else if (field == "qi") { comp = mi.qi; }
527  else if (field == "qr") { comp = mi.qr; }
528  else if (field == "qs") { comp = mi.qs; }
529  else if (field == "qg") { comp = mi.qg; }
530  else if (field == "qt") {
531  comp = (mi.qc >= 0 || mi.qi >= 0 || mi.qr >= 0 ||
532  mi.qs >= 0 || mi.qg >= 0) ? 0 : -1;
533  }
534  if (comp < 0) {
535  amrex::Abort("Refinement field_name '" + field +
536  "' is not available for the configured moisture model");
537  }
538  }
539  }
540 
541  if (ppr.countval("value_greater")) {
542  int num_val = ppr.countval("value_greater");
543  Vector<Real> value(num_val);
544  ppr.getarr("value_greater",value,0,num_val);
545  ref_tags.push_back(AMRErrorTag(value,AMRErrorTag::GREATER,field,info));
546  }
547  else if (ppr.countval("value_less"))
548  {
549  int num_val = ppr.countval("value_less");
550  Vector<Real> value(num_val);
551  ppr.getarr("value_less",value,0,num_val);
552  ref_tags.push_back(AMRErrorTag(value,AMRErrorTag::LESS,field,info));
553  }
554  else if (ppr.countval("adjacent_difference_greater"))
555  {
556  int num_val = ppr.countval("adjacent_difference_greater");
557  Vector<Real> value(num_val);
558  ppr.getarr("adjacent_difference_greater",value,0,num_val);
559  ref_tags.push_back(AMRErrorTag(value,AMRErrorTag::GRAD,field,info));
560  }
561  else if (real_box.ok())
562  {
563  ref_tags.push_back(AMRErrorTag(info));
564  }
565  else if ( (lev_for_box > 0) && (refinement_indicators[i] != "storm_tracker") )
566  {
567  Abort(std::string("Unrecognized refinement indicator for " + refinement_indicators[i]).c_str());
568  }
569  } // loop over criteria
570  } // if max_level > 0
571 }
void read_box_for_refinement(std::string &ref_prefix, int &lev_for_box, amrex::RealBox &real_box)
Definition: ERF_RefineBox.cpp:6

Referenced by ERF_shared().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remake_zphys()

void ERF::remake_zphys ( int  lev,
std::unique_ptr< amrex::MultiFab > &  temp_zphys_nd 
)
797 {
798  if (solverChoice.init_type != InitType::WRFInput && solverChoice.init_type != InitType::Metgrid)
799  {
800  if (lev == 0) {
801  temp_zphys_nd->ParallelCopy(*z_phys_nd[lev], 0, 0, 1, z_phys_nd[lev]->nGrowVect(), z_phys_nd[lev]->nGrowVect());
802  } else {
803  //
804  // First interpolate from coarser level
805  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
806  // have been pre-filled - this includes ghost cells both inside and outside
807  // the domain
808  //
809  InterpFromCoarseLevel(*temp_zphys_nd, z_phys_nd[lev]->nGrowVect(),
810  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
811  *z_phys_nd[lev-1], 0, 0, 1,
812  geom[lev-1], geom[lev],
813  refRatio(lev-1), &node_bilinear_interp,
815 
816  // This recomputes the fine values using the bottom terrain at the fine resolution,
817  // and also fills values of z_phys_nd outside the domain
818  make_terrain_fitted_coords(lev,geom[lev],*temp_zphys_nd,zlevels_stag[lev],phys_bc_type);
819 
820  } // lev > 0
821 
822  std::swap(temp_zphys_nd, z_phys_nd[lev]);
823 
824  } else {
825  if (lev == 0) {
826  temp_zphys_nd->ParallelCopy(*z_phys_nd[lev], 0, 0, 1, z_phys_nd[lev]->nGrowVect(), z_phys_nd[lev]->nGrowVect());
827  } else {
828  //
829  // First interpolate from coarser level
830  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
831  // have been pre-filled - this includes ghost cells both inside and outside
832  // the domain
833  //
834  InterpFromCoarseLevel(*temp_zphys_nd, z_phys_nd[lev]->nGrowVect(),
835  z_phys_nd[lev]->nGrowVect(), // DO fill ghost cells outside the domain
836  *z_phys_nd[lev-1], 0, 0, 1,
837  geom[lev-1], geom[lev],
838  refRatio(lev-1), &node_bilinear_interp,
840 
841  } // lev > 0
842 
843  std::swap(temp_zphys_nd, z_phys_nd[lev]);
844  }
845 
846  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
847  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
848  //
849  // This assumes we have already remade the EBGeometry
850  //
851  terrain_blanking[lev]->setVal(one);
852  MultiFab::Subtract(*terrain_blanking[lev], EBFactory(lev).getVolFrac(), 0, 0, 1, z_phys_nd[lev]->nGrowVect());
853  }
854 
855  // Compute the min dz and pass to the micro model
856  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
857  micro->Set_dzmin(lev, dzmin);
858 }
Here is the call graph for this function:

◆ RemakeLevel()

void ERF::RemakeLevel ( int  lev,
amrex::Real  time,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
override
536 {
537  //
538  // Note that "time" here is elapsed time
539  //
540  if (verbose) {
541  amrex::Print() <<" REMAKING WITH NEW BA AT LEVEL " << lev << " " << ba << std::endl;
542  }
543 
544  AMREX_ALWAYS_ASSERT(solverChoice.terrain_type != TerrainType::MovingFittedMesh);
545 
546  BoxArray ba_old(vars_new[lev][Vars::cons].boxArray());
547  DistributionMapping dm_old(vars_new[lev][Vars::cons].DistributionMap());
548 
549  if (verbose) {
550  amrex::Print() <<" OLD BA AT LEVEL " << lev << " " << ba_old << std::endl;
551  }
552 
553  //
554  // Re-define subdomain at this level within the domain such that
555  // 1) all boxes in a given subdomain are "connected"
556  // 2) no boxes in a subdomain touch any boxes in any other subdomain
557  //
558  subdomains[lev].clear();
559  make_subdomains(ba.simplified_list(), subdomains[lev]);
560 
561  int ncomp_cons = vars_new[lev][Vars::cons].nComp();
562  IntVect ngrow_state = vars_new[lev][Vars::cons].nGrowVect();
563 
564  int ngrow_vels = ComputeGhostCells(solverChoice);
565 
566  Vector<MultiFab> temp_lev_new(Vars::NumTypes);
567  Vector<MultiFab> temp_lev_old(Vars::NumTypes);
568  MultiFab temp_base_state;
569 
570  std::unique_ptr<MultiFab> temp_zphys_nd;
571 
572  //********************************************************************************************
573  // This allocates all kinds of things, including but not limited to: solution arrays,
574  // terrain arrays and metrics, and base state.
575  // *******************************************************************************************
576  init_stuff(lev, ba, dm, temp_lev_new, temp_lev_old, temp_base_state, temp_zphys_nd);
577 
578  // ********************************************************************************************
579  // Build the data structures for terrain-related quantities
580  // ********************************************************************************************
581  if ( solverChoice.terrain_type == TerrainType::EB ||
582  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
583  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
584  {
585  const amrex::EB2::IndexSpace& ebis = amrex::EB2::IndexSpace::top();
586  const EB2::Level& eb_level = ebis.getLevel(geom[lev]);
587  if (solverChoice.terrain_type == TerrainType::EB) {
588  eb[lev]->make_all_factories(lev, geom[lev], ba, dm, eb_level);
589  } else if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
590  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
591  eb[lev]->make_cc_factory(lev, geom[lev], ba, dm, eb_level);
592  }
593  }
594  remake_zphys(lev, temp_zphys_nd);
596 
597  // ********************************************************************************************
598  // Make sure that detJ and z_phys_cc are the average of the data on a finer level if there is one
599  // Note that this shouldn't be necessary because the fine grid is created by interpolation
600  // from the coarse ... but just in case ...
601  // ********************************************************************************************
602  if ( (SolverChoice::mesh_type != MeshType::ConstantDz) && (solverChoice.coupling_type == CouplingType::TwoWay) ) {
603  for (int crse_lev = lev-1; crse_lev >= 0; crse_lev--) {
604  average_down( *detJ_cc[crse_lev+1], *detJ_cc[crse_lev], 0, 1, refRatio(crse_lev));
605  average_down(*z_phys_cc[crse_lev+1], *z_phys_cc[crse_lev], 0, 1, refRatio(crse_lev));
606  }
607  }
608 
609  // ********************************************************************************************
610  // Build the data structures for canopy model (depends upon z_phys)
611  // ********************************************************************************************
613  m_forest_drag[lev]->define_drag_field(ba, dm, geom[lev], z_phys_cc[lev].get(), z_phys_nd[lev].get());
614  }
615 
616  // *****************************************************************************************************
617  // Create the physbcs objects (after initializing the terrain but before calling FillCoarsePatch
618  // *****************************************************************************************************
619  make_physbcs(lev);
620 
621  // ********************************************************************************************
622  // Update the base state at this level by interpolation from coarser level AND copy
623  // from previous (pre-regrid) base_state array
624  // ********************************************************************************************
625  if (lev > 0) {
626  Interpolater* mapper = &cell_cons_interp;
627 
628  Vector<MultiFab*> fmf = {&base_state[lev ], &base_state[lev ]};
629  Vector<MultiFab*> cmf = {&base_state[lev-1], &base_state[lev-1]};
630  Vector<Real> ftime = {time, time};
631  Vector<Real> ctime = {time, time};
632 
633  // Call FillPatch which ASSUMES that all ghost cells at lev-1 have already been filled
634  FillPatchTwoLevels(temp_base_state, temp_base_state.nGrowVect(), IntVect(0,0,0),
635  time, cmf, ctime, fmf, ftime,
636  0, 0, temp_base_state.nComp(), geom[lev-1], geom[lev],
637  refRatio(lev-1), mapper, domain_bcs_type,
639 
640  // Impose bc's outside the domain
641  (*physbcs_base[lev])(temp_base_state,0,temp_base_state.nComp(),base_state[lev].nGrowVect());
642 
643  // *************************************************************************************************
644  // This will fill the temporary MultiFabs with data from vars_new
645  // NOTE: the momenta here are only used as scratch space, the momenta themselves are not fillpatched
646  // NOTE: we must create the new base state before calling FillPatch because we will
647  // interpolate perturbational quantities
648  // *************************************************************************************************
649  FillPatchFineLevel(lev, time, {&temp_lev_new[Vars::cons],&temp_lev_new[Vars::xvel],
650  &temp_lev_new[Vars::yvel],&temp_lev_new[Vars::zvel]},
651  {&temp_lev_new[Vars::cons],&rU_new[lev],&rV_new[lev],&rW_new[lev]},
652  base_state[lev], temp_base_state, false);
653  } else {
654  temp_base_state.ParallelCopy(base_state[lev],0,0,base_state[lev].nComp(),
655  base_state[lev].nGrowVect(),base_state[lev].nGrowVect());
656  temp_lev_new[Vars::cons].ParallelCopy(vars_new[lev][Vars::cons],0,0,ncomp_cons,ngrow_state,ngrow_state);
657  temp_lev_new[Vars::xvel].ParallelCopy(vars_new[lev][Vars::xvel],0,0, 1,ngrow_vels,ngrow_vels);
658  temp_lev_new[Vars::yvel].ParallelCopy(vars_new[lev][Vars::yvel],0,0, 1,ngrow_vels,ngrow_vels);
659 
660  temp_lev_new[Vars::zvel].setVal(0.);
661  temp_lev_new[Vars::zvel].ParallelCopy(vars_new[lev][Vars::zvel],0,0, 1,
662  IntVect(ngrow_vels,ngrow_vels,0),IntVect(ngrow_vels,ngrow_vels,0));
663  }
664 
665  // Now swap the pointers since we needed both old and new in the FillPatch
666  std::swap(temp_base_state, base_state[lev]);
667 
668  // ********************************************************************************************
669  // Copy from new into old just in case
670  // ********************************************************************************************
671  MultiFab::Copy(temp_lev_old[Vars::cons],temp_lev_new[Vars::cons],0,0,ncomp_cons,ngrow_state);
672  MultiFab::Copy(temp_lev_old[Vars::xvel],temp_lev_new[Vars::xvel],0,0, 1,ngrow_vels);
673  MultiFab::Copy(temp_lev_old[Vars::yvel],temp_lev_new[Vars::yvel],0,0, 1,ngrow_vels);
674  MultiFab::Copy(temp_lev_old[Vars::zvel],temp_lev_new[Vars::zvel],0,0, 1,IntVect(ngrow_vels,ngrow_vels,0));
675 
676  // ********************************************************************************************
677  // Now swap the pointers
678  // ********************************************************************************************
679  for (int var_idx = 0; var_idx < Vars::NumTypes; ++var_idx) {
680  std::swap(temp_lev_new[var_idx], vars_new[lev][var_idx]);
681  std::swap(temp_lev_old[var_idx], vars_old[lev][var_idx]);
682  }
683 
684  //
685  // Note that t_new = time here is elapsed time
686  //
687  t_new[lev] = time;
688  t_old[lev] = time - bogus_large_value;
689 
690  // ********************************************************************************************
691  // Build the data structures for calculating diffusive/turbulent terms
692  // ********************************************************************************************
693  update_diffusive_arrays(lev, ba, dm);
694 
695  //********************************************************************************************
696  // Microphysics
697  // *******************************************************************************************
698  int q_size = micro->Get_Qmoist_Size(lev);
699  qmoist[lev].resize(q_size);
700  micro->Define(lev, solverChoice);
701  if (solverChoice.moisture_type != MoistureType::None)
702  {
703  micro->Init(lev, vars_new[lev][Vars::cons],
704  grids[lev], Geom(lev), zero,
705  z_phys_nd[lev], detJ_cc[lev]); // dummy dt value
706  }
707  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
708  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
709  }
710 
711  //********************************************************************************************
712  // Radiation
713  // *******************************************************************************************
714  if (solverChoice.rad_type != RadiationType::None)
715  {
716  rad[lev]->Init(geom[lev], ba, &vars_new[lev][Vars::cons]);
717  }
718 
719  // ********************************************************************************************
720  // Initialize the integrator class
721  // ********************************************************************************************
723 
724  // We need to re-define the FillPatcher if the grids have changed
725  if (lev > 0 && cf_width >= 0) {
726  bool ba_changed = (ba != ba_old);
727  bool dm_changed = (dm != dm_old);
728  if (ba_changed || dm_changed) {
730  }
731  }
732 
733  // These calls are done in AmrCore::regrid if this is a regrid at lev > 0
734  // For a level 0 regrid we must explicitly do them here
735  if (lev == 0) {
736  // Define grids[lev] to be ba
737  SetBoxArray(lev, ba);
738 
739  // Define dmap[lev] to be dm
740  SetDistributionMap(lev, dm);
741  }
742 
743  // ********************************************************************************************
744  // Initialize the 2D data structures
745  // ********************************************************************************************
746  // NOTE: 2D MFs must be filled before SurfaceLayer is defined since SL class uses sst/tsk
747  // Clear the 2D arrays
748  if (sst_lev[lev][0]) {
749  for (int n = 0; n < sst_lev[lev].size(); n++) {
750  sst_lev[lev][n].reset();
751  }
752  }
753  if (tsk_lev[lev][0]) {
754  for (int n = 0; n < tsk_lev[lev].size(); n++) {
755  tsk_lev[lev][n].reset();
756  }
757  }
758  if (lat_m[lev]) {
759  lat_m[lev].reset();
760  }
761  if (lon_m[lev]) {
762  lon_m[lev].reset();
763  }
764  if (sinPhi_m[lev]) {
765  sinPhi_m[lev].reset();
766  }
767  if (cosPhi_m[lev]) {
768  cosPhi_m[lev].reset();
769  }
770 
771  //
772  // Interpolate the 2D arrays at the lower boundary. We assume that since we created
773  // them by interpolation it is ok just to recreate them by interpolation.
774  // Note that ba2d is constructed already in init_stuff, but we have not yet defined dmap[lev]
775  // so we must explicitly pass dm.
776  Interp2DArrays(lev,ba2d[lev],dm);
777 
778  // ********************************************************************************************
779  // Update the SurfaceLayer arrays at this level
780  // ********************************************************************************************
781  if (m_SurfaceLayer != nullptr) {
782  if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer) {
783  int nlevs = finest_level+1;
784  Vector<MultiFab*> mfv_old = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::xvel],
785  &vars_old[lev][Vars::yvel], &vars_old[lev][Vars::zvel]};
786  m_SurfaceLayer->make_SurfaceLayer_at_level(lev,nlevs,
787  mfv_old, Theta_prim[lev], Qv_prim[lev],
788  Qr_prim[lev], z_phys_nd[lev],
789  Hwave[lev].get(),Lwave[lev].get(),eddyDiffs_lev[lev].get(),
791  sst_lev[lev], tsk_lev[lev], lmask_lev[lev]);
792  }
793  }
794 
795  // ********************************************************************************************
796  // Set up the Rayleigh damping vectors at this (new) level
797  // ********************************************************************************************
800  {
802  }
803 
804  // Particle redistribute handled in timeStep() after regrid() completes.
805  // Calling it here causes stale-grid crashes.
806 }
void remake_zphys(int lev, std::unique_ptr< amrex::MultiFab > &temp_zphys_nd)
Definition: ERF_MakeNewArrays.cpp:796
Here is the call graph for this function:

◆ restart()

void ERF::restart ( )
1757 {
1758  auto dRestartTime0 = amrex::second();
1759 
1761 
1762  // Force regrid on level 0 if more procs than boxes are requested
1764  grids[0].size() < ParallelDescriptor::NProcs() );
1765 
1767  //
1768  // Coarsening before we split the grids ensures that each resulting
1769  // grid will have an even number of cells in each direction.
1770  //
1771  BoxArray new_ba(amrex::coarsen(Geom(0).Domain(),2));
1772  //
1773  // Now split up into list of grids within max_grid_size[0] limit.
1774  //
1775  new_ba.maxSize(max_grid_size[0]/2);
1776  //
1777  // Now refine these boxes back to level zero
1778  //
1779  new_ba.refine(2);
1780 
1781  if (refine_grid_layout) {
1782  ChopGrids(0, new_ba, ParallelDescriptor::NProcs());
1783  }
1784 
1785  if (new_ba != grids[0]) {
1786  DistributionMapping new_dm(new_ba);
1787  RemakeLevel(0,t_new[0],new_ba,new_dm);
1788  }
1789  }
1790 
1791 #ifdef ERF_USE_PARTICLES
1792  // We call this here without knowing whether the particles have already been initialized or not
1793  initializeTracers((ParGDBBase*)GetParGDB(),z_phys_nd,t_new[0]);
1794 #endif
1795 
1796  Real cur_time = t_new[0];
1797  if (m_check_per > zero) {last_check_file_time = cur_time;}
1798  if (m_plot2d_per_1 > zero) {last_plot2d_file_time_1 = std::floor(cur_time/m_plot2d_per_1) * m_plot2d_per_1;}
1799  if (m_plot2d_per_2 > zero) {last_plot2d_file_time_2 = std::floor(cur_time/m_plot2d_per_2) * m_plot2d_per_2;}
1800  if (m_plot3d_per_1 > zero) {last_plot3d_file_time_1 = std::floor(cur_time/m_plot3d_per_1) * m_plot3d_per_1;}
1801  if (m_plot3d_per_2 > zero) {last_plot3d_file_time_2 = std::floor(cur_time/m_plot3d_per_2) * m_plot3d_per_2;}
1802 
1808 
1809  if (verbose > 0)
1810  {
1811  auto dRestartTime = amrex::second() - dRestartTime0;
1812  ParallelDescriptor::ReduceRealMax(dRestartTime,ParallelDescriptor::IOProcessorNumber());
1813  amrex::Print() << "Restart time = " << dRestartTime << " seconds." << '\n';
1814  }
1815 }
void RemakeLevel(int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
Definition: ERF_MakeNewLevel.cpp:535
void ReadCheckpointFile()
Definition: ERF_Checkpoint.cpp:504

◆ sample_lines()

void ERF::sample_lines ( int  lev,
amrex::Real  time,
amrex::IntVect  cell,
amrex::MultiFab &  mf 
)

Utility function for sampling data along a line along the z-dimension at the (x,y) indices specified and writes it to an output file.

Parameters
levCurrent level
timeCurrent time
cellIntVect containing the x,y-dimension indices to sample along z
mfMultiFab from which we sample the data
565 {
566  int ifile = 0;
567 
568  const int ncomp = mf.nComp(); // cell-centered state vars
569 
570  MultiFab mf_vels(grids[lev], dmap[lev], AMREX_SPACEDIM, 0);
571  average_face_to_cellcenter(mf_vels, 0,
572  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],&vars_new[lev][Vars::yvel],&vars_new[lev][Vars::zvel]});
573 
574  //
575  // Sample the data at a line (in direction "dir") in space
576  // In this case we sample in the vertical direction so dir = 2
577  // The "k" value of "cell" is ignored
578  //
579  int dir = 2;
580  MultiFab my_line = get_line_data(mf, dir, cell);
581  MultiFab my_line_vels = get_line_data(mf_vels, dir, cell);
582  MultiFab my_line_tau11 = get_line_data(*Tau[lev][TauType::tau11], dir, cell);
583  MultiFab my_line_tau12 = get_line_data(*Tau[lev][TauType::tau12], dir, cell);
584  MultiFab my_line_tau13 = get_line_data(*Tau[lev][TauType::tau13], dir, cell);
585  MultiFab my_line_tau22 = get_line_data(*Tau[lev][TauType::tau22], dir, cell);
586  MultiFab my_line_tau23 = get_line_data(*Tau[lev][TauType::tau23], dir, cell);
587  MultiFab my_line_tau33 = get_line_data(*Tau[lev][TauType::tau33], dir, cell);
588 
589  for (MFIter mfi(my_line, false); mfi.isValid(); ++mfi)
590  {
591  // HERE DO WHATEVER YOU WANT TO THE DATA BEFORE WRITING
592 
593  std::ostream& sample_log = SampleLineLog(ifile);
594  if (sample_log.good()) {
595  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << time;
596  const auto& my_line_arr = my_line[0].const_array();
597  const auto& my_line_vels_arr = my_line_vels[0].const_array();
598  const auto& my_line_tau11_arr = my_line_tau11[0].const_array();
599  const auto& my_line_tau12_arr = my_line_tau12[0].const_array();
600  const auto& my_line_tau13_arr = my_line_tau13[0].const_array();
601  const auto& my_line_tau22_arr = my_line_tau22[0].const_array();
602  const auto& my_line_tau23_arr = my_line_tau23[0].const_array();
603  const auto& my_line_tau33_arr = my_line_tau33[0].const_array();
604  const Box& my_box = my_line[0].box();
605  const int klo = my_box.smallEnd(2);
606  const int khi = my_box.bigEnd(2);
607  int i = cell[0];
608  int j = cell[1];
609  for (int n = 0; n < ncomp; n++) {
610  for (int k = klo; k <= khi; k++) {
611  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_arr(i,j,k,n);
612  }
613  }
614  for (int n = 0; n < AMREX_SPACEDIM; n++) {
615  for (int k = klo; k <= khi; k++) {
616  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_vels_arr(i,j,k,n);
617  }
618  }
619  for (int k = klo; k <= khi; k++) {
620  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_tau11_arr(i,j,k);
621  }
622  for (int k = klo; k <= khi; k++) {
623  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_tau12_arr(i,j,k);
624  }
625  for (int k = klo; k <= khi; k++) {
626  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_tau13_arr(i,j,k);
627  }
628  for (int k = klo; k <= khi; k++) {
629  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_tau22_arr(i,j,k);
630  }
631  for (int k = klo; k <= khi; k++) {
632  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_tau23_arr(i,j,k);
633  }
634  for (int k = klo; k <= khi; k++) {
635  sample_log << std::setw(datwidth) << std::setprecision(datprecision) << my_line_tau33_arr(i,j,k);
636  }
637  sample_log << std::endl;
638  } // if good
639  } // mfi
640 }
const int datwidth
Definition: ERF.H:1127
AMREX_FORCE_INLINE std::ostream & SampleLineLog(int i)
Definition: ERF.H:1568
const int datprecision
Definition: ERF.H:1128

◆ sample_points()

void ERF::sample_points ( int  lev,
amrex::Real  time,
amrex::IntVect  cell,
amrex::MultiFab &  mf 
)

Utility function for sampling MultiFab data at a specified cell index.

Parameters
levLevel for the associated MultiFab data
timeCurrent time
cellIntVect containing the indexes for the cell where we want to sample
mfMultiFab from which we wish to sample data
529 {
530  int ifile = 0;
531 
532  //
533  // Sample the data at a single point in space
534  //
535  int ncomp = mf.nComp();
536  Vector<Real> my_point = get_cell_data(mf, cell);
537 
538  if (!my_point.empty()) {
539 
540  // HERE DO WHATEVER YOU WANT TO THE DATA BEFORE WRITING
541 
542  std::ostream& sample_log = SamplePointLog(ifile);
543  if (sample_log.good()) {
544  sample_log << std::setw(datwidth) << time;
545  for (int i = 0; i < ncomp; ++i)
546  {
547  sample_log << std::setw(datwidth) << my_point[i];
548  }
549  sample_log << std::endl;
550  } // if good
551  } // only write from processor that holds the cell
552 }
AMREX_FORCE_INLINE std::ostream & SamplePointLog(int i)
Definition: ERF.H:1554

◆ SampleLine()

amrex::IntVect& ERF::SampleLine ( int  i)
inlineprivate
1595  {
1596  return sampleline[i];
1597  }

◆ SampleLineLog()

AMREX_FORCE_INLINE std::ostream& ERF::SampleLineLog ( int  i)
inlineprivate
1569  {
1570  return *samplelinelog[i];
1571  }

◆ SampleLineLogName()

std::string ERF::SampleLineLogName ( int  i) const
inlineprivatenoexcept

The filename of the ith samplelinelog file.

1728 { return samplelinelogname[i]; }

◆ SamplePoint()

amrex::IntVect& ERF::SamplePoint ( int  i)
inlineprivate
1582  {
1583  return samplepoint[i];
1584  }

◆ SamplePointLog()

AMREX_FORCE_INLINE std::ostream& ERF::SamplePointLog ( int  i)
inlineprivate
1555  {
1556  return *sampleptlog[i];
1557  }

◆ SamplePointLogName()

std::string ERF::SamplePointLogName ( int  i) const
inlineprivatenoexcept

The filename of the ith sampleptlog file.

1725 { return sampleptlogname[i]; }

◆ setPlotVariables()

void ERF::setPlotVariables ( const std::string &  pp_plot_var_names,
amrex::Vector< std::string > &  plot_var_names 
)
private
15 {
16  ParmParse pp(pp_prefix);
17 
18  if (pp.contains(pp_plot_var_names.c_str()))
19  {
20  std::string nm;
21 
22  int nPltVars = pp.countval(pp_plot_var_names.c_str());
23 
24  for (int i = 0; i < nPltVars; i++)
25  {
26  pp.get(pp_plot_var_names.c_str(), nm, i);
27 
28  // Add the named variable to our list of plot variables
29  // if it is not already in the list
30  if (!containerHasElement(plot_var_names, nm)) {
31  plot_var_names.push_back(nm);
32  }
33  }
34  } else {
35  //
36  // The default is to add none of the variables to the list
37  //
38  plot_var_names.clear();
39  }
40 
41  // Get state variables in the same order as we define them,
42  // since they may be in any order in the input list
43  Vector<std::string> tmp_plot_names;
44 
45  for (int i = 0; i < cons_names.size(); ++i) {
46  if ( containerHasElement(plot_var_names, cons_names[i]) ) {
47  if (solverChoice.moisture_type == MoistureType::None) {
48  if (cons_names[i] != "rhoQ1" && cons_names[i] != "rhoQ2" && cons_names[i] != "rhoQ3" &&
49  cons_names[i] != "rhoQ4" && cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
50  {
51  tmp_plot_names.push_back(cons_names[i]);
52  }
53  } else if (solverChoice.moisture_type == MoistureType::Kessler) { // allow rhoQ1, rhoQ2, rhoQ3
54  if (cons_names[i] != "rhoQ4" && cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
55  {
56  tmp_plot_names.push_back(cons_names[i]);
57  }
58  } else if ( (solverChoice.moisture_type == MoistureType::SatAdj) ||
59  (solverChoice.moisture_type == MoistureType::SAM_NoPrecip_NoIce) ||
60  (solverChoice.moisture_type == MoistureType::Kessler_NoRain) ) { // allow rhoQ1, rhoQ2
61  if (cons_names[i] != "rhoQ3" && cons_names[i] != "rhoQ4" &&
62  cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
63  {
64  tmp_plot_names.push_back(cons_names[i]);
65  }
66  } else if ( (solverChoice.moisture_type == MoistureType::Morrison_NoIce) ||
67  (solverChoice.moisture_type == MoistureType::SAM_NoIce ) ) { // allow rhoQ1, rhoQ2, rhoQ4
68  if (cons_names[i] != "rhoQ3" && cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
69  {
70  tmp_plot_names.push_back(cons_names[i]);
71  }
72  } else
73  {
74  // For moisture_type SAM, Morrison and WSM6 we have all six variables
75  tmp_plot_names.push_back(cons_names[i]);
76  }
77  }
78  }
79 
80  // check for velocity since it's not in cons_names
81  // if we are asked for any velocity component, we will need them all
82  if (containerHasElement(plot_var_names, "x_velocity") ||
83  containerHasElement(plot_var_names, "y_velocity") ||
84  containerHasElement(plot_var_names, "z_velocity")) {
85  tmp_plot_names.push_back("x_velocity");
86  tmp_plot_names.push_back("y_velocity");
87  tmp_plot_names.push_back("z_velocity");
88  }
89 
90  //
91  // If the model we are running doesn't have the variable listed in the inputs file,
92  // just ignore it rather than aborting
93  //
94  for (int i = 0; i < derived_names.size(); ++i) {
95  if ( containerHasElement(plot_var_names, derived_names[i]) ) {
96  bool ok_to_add = ( (solverChoice.terrain_type == TerrainType::ImmersedForcing || solverChoice.buildings_type == BuildingsType::ImmersedForcing ) ||
97  (derived_names[i] != "terrain_IB_mask") );
98  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
99  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
100  (derived_names[i] != "detJ") );
101  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
102  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
103  (derived_names[i] != "z_phys") );
104 #ifndef ERF_USE_WINDFARM
105  ok_to_add &= (derived_names[i] != "SMark0" && derived_names[i] != "SMark1");
106 #endif
107  if (ok_to_add)
108  {
109  if (solverChoice.moisture_type == MoistureType::None) { // no moist quantities allowed
110  if (derived_names[i] != "qv" && derived_names[i] != "qc" && derived_names[i] != "qrain" &&
111  derived_names[i] != "qi" && derived_names[i] != "qsnow" && derived_names[i] != "qgraup" &&
112  derived_names[i] != "qt" && derived_names[i] != "qn" && derived_names[i] != "qp" &&
113  derived_names[i] != "rain_accum" && derived_names[i] != "snow_accum" && derived_names[i] != "graup_accum")
114  {
115  tmp_plot_names.push_back(derived_names[i]);
116  }
117  } else if ( (solverChoice.moisture_type == MoistureType::Kessler ) ||
118  (solverChoice.moisture_type == MoistureType::Morrison_NoIce) ||
119  (solverChoice.moisture_type == MoistureType::SAM_NoIce ) ) { // allow qv, qc, qrain
120  if (derived_names[i] != "qi" && derived_names[i] != "qsnow" && derived_names[i] != "qgraup" &&
121  derived_names[i] != "snow_accum" && derived_names[i] != "graup_accum")
122  {
123  tmp_plot_names.push_back(derived_names[i]);
124  }
125  } else if ( (solverChoice.moisture_type == MoistureType::SatAdj) ||
126  (solverChoice.moisture_type == MoistureType::SAM_NoPrecip_NoIce) ||
127  (solverChoice.moisture_type == MoistureType::Kessler_NoRain) ||
128  (solverChoice.moisture_type == MoistureType::MoistNoCondensation) ) { // allow qv, qc
129  if (derived_names[i] != "qrain" && derived_names[i] != "qi" && derived_names[i] != "qsnow" &&
130  derived_names[i] != "qgraup" && derived_names[i] != "qp" &&
131  derived_names[i] != "rain_accum" && derived_names[i] != "snow_accum" && derived_names[i] != "graup_accum")
132  {
133  tmp_plot_names.push_back(derived_names[i]);
134  }
135  } else
136  {
137  // For moisture_type SAM and Morrison we have all moist quantities
138  tmp_plot_names.push_back(derived_names[i]);
139  }
140  } // use_terrain?
141  } // hasElement
142  }
143 
144 #ifdef ERF_USE_WINDFARM
145  for (int i = 0; i < derived_names.size(); ++i) {
146  if ( containerHasElement(plot_var_names, derived_names[i]) ) {
147  if(solverChoice.windfarm_type == WindFarmType::Fitch or solverChoice.windfarm_type == WindFarmType::EWP) {
148  if(derived_names[i] == "num_turb" or derived_names[i] == "SMark0") {
149  tmp_plot_names.push_back(derived_names[i]);
150  }
151  }
152  if( solverChoice.windfarm_type == WindFarmType::SimpleAD or
153  solverChoice.windfarm_type == WindFarmType::GeneralAD ) {
154  if(derived_names[i] == "num_turb" or derived_names[i] == "SMark0" or derived_names[i] == "SMark1") {
155  tmp_plot_names.push_back(derived_names[i]);
156  }
157  }
158  }
159  }
160 #endif
161 
162 #ifdef ERF_USE_PARTICLES
163  const auto& particles_namelist( particleData.getNamesUnalloc() );
164  for (auto it = particles_namelist.cbegin(); it != particles_namelist.cend(); ++it) {
165  std::string tmp( (*it)+"_count" );
166  if (containerHasElement(plot_var_names, tmp) ) {
167  tmp_plot_names.push_back(tmp);
168  }
169  }
170 #endif
171 
172  plot_var_names = tmp_plot_names;
173 }
const amrex::Vector< std::string > derived_names
Definition: ERF.H:1216
const amrex::Vector< std::string > cons_names
Definition: ERF.H:1207

Referenced by ERF_shared().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setPlotVariables2D()

void ERF::setPlotVariables2D ( const std::string &  pp_plot_var_names,
amrex::Vector< std::string > &  plot_var_names 
)
private
94 {
95  ParmParse pp(pp_prefix);
96 
97  if (!pp.contains(pp_plot_var_names.c_str())) {
98  //
99  // The default is to add none of the variables to the list
100  //
101  plot_var_names.clear();
102  return;
103  }
104 
105  Vector<std::string> requested_plot_names;
106  std::string nm;
107  const int nPltVars = pp.countval(pp_plot_var_names.c_str());
108  for (int i = 0; i < nPltVars; ++i) {
109  pp.get(pp_plot_var_names.c_str(), nm, i);
110  requested_plot_names.push_back(nm);
111  }
112 
113  const auto available_names = plotfile2d::diagnostic_names();
114 
115  // Keep the canonical built-in 2D ordering so the plotfile component layout
116  // stays stable even if the input request order changes.
117  const auto selection = plotfile2d::select_requested_plot_variables(requested_plot_names,
118  available_names);
119  plot_var_names = selection.accepted;
120 
121  // Unknown 2D names are skipped rather than aborting because the 2D plot
122  // list is intentionally user-configurable and may include names that are not
123  // compiled into a given build. The warning is still explicit so the user
124  // can correct the input deck.
125  warn_for_unavailable_2d_plot_vars(plotfile2d::format_plot2d_parameter_name(pp_prefix, pp_plot_var_names),
126  selection.unavailable, available_names);
127 }
amrex::Vector< std::string > diagnostic_names()
Definition: ERF_Plotfile2DCatalog.cpp:62
std::string format_plot2d_parameter_name(const std::string &pp_prefix, const std::string &parameter_name)
Definition: ERF_Plotfile2DUtils.cpp:75
PlotVariableSelection select_requested_plot_variables(const amrex::Vector< std::string > &requested, const amrex::Vector< std::string > &available)
Definition: ERF_Plotfile2DUtils.cpp:31

Referenced by ERF_shared().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setRayleighRefFromSounding()

void ERF::setRayleighRefFromSounding ( bool  restarting)
private

Set Rayleigh mean profiles from input sounding.

Sets the Rayleigh Damping averaged quantities from an externally supplied input sounding data file.

Parameters
[in]restartingBoolean parameter that indicates whether we are currently restarting from a checkpoint file.
84 {
85  // If we are restarting then we haven't read the input_sounding file yet
86  // so we need to read it here
87  // TODO: should we store this information in the checkpoint file instead?
88  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
89  if (restarting) {
91  for (int n = 0; n < input_sounding_data.n_sounding_files; n++) {
92  input_sounding_data.read_from_file(geom[0], zlevels_stag[0], n, is_moist);
93  }
94  }
95 
96  const Real* z_inp_sound = input_sounding_data.z_inp_sound[0].dataPtr();
97  const Real* U_inp_sound = input_sounding_data.U_inp_sound[0].dataPtr();
98  const Real* V_inp_sound = input_sounding_data.V_inp_sound[0].dataPtr();
99  const Real* theta_inp_sound = input_sounding_data.theta_inp_sound[0].dataPtr();
100  const int inp_sound_size = input_sounding_data.size(0);
101 
102  int refine_fac{1};
103  for (int lev = 0; lev <= finest_level; lev++)
104  {
105  const int klo = geom[lev].Domain().smallEnd(2);
106  const int khi = geom[lev].Domain().bigEnd(2);
107  const int Nz = khi - klo + 1;
108 
109  Vector<Real> zcc(Nz);
110  Vector<Real> zlevels_sub(zlevels_stag[0].begin()+klo/refine_fac,
111  zlevels_stag[0].begin()+khi/refine_fac+2);
112  expand_and_interpolate_1d(zcc, zlevels_sub, refine_fac, true);
113 #if 0
114  amrex::AllPrint() << "lev="<<lev<<" : (refine_fac="<<refine_fac<<",klo="<<klo<<",khi="<<khi<<") ";
115  for (int k = 0; k < zlevels_sub.size(); k++) { amrex::AllPrint() << zlevels_sub[k] << " "; }
116  amrex::AllPrint() << " --> ";
117  for (int k = 0; k < Nz; k++) { amrex::AllPrint() << zcc[k] << " "; }
118  amrex::AllPrint() << std::endl;
119 #endif
120 
121  for (int k = 0; k < Nz; k++)
122  {
123  h_rayleigh_ptrs[lev][Rayleigh::ubar][k] = interpolate_1d(z_inp_sound, U_inp_sound, zcc[k], inp_sound_size);
124  h_rayleigh_ptrs[lev][Rayleigh::vbar][k] = interpolate_1d(z_inp_sound, V_inp_sound, zcc[k], inp_sound_size);
126  h_rayleigh_ptrs[lev][Rayleigh::thetabar][k] = interpolate_1d(z_inp_sound, theta_inp_sound, zcc[k], inp_sound_size);
127  }
128 
129  // Copy from host version to device version
130  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::ubar].begin(), h_rayleigh_ptrs[lev][Rayleigh::ubar].end(),
131  d_rayleigh_ptrs[lev][Rayleigh::ubar].begin());
132  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::vbar].begin(), h_rayleigh_ptrs[lev][Rayleigh::vbar].end(),
133  d_rayleigh_ptrs[lev][Rayleigh::vbar].begin());
134  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::wbar].begin(), h_rayleigh_ptrs[lev][Rayleigh::wbar].end(),
135  d_rayleigh_ptrs[lev][Rayleigh::wbar].begin());
136  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::thetabar].begin(), h_rayleigh_ptrs[lev][Rayleigh::thetabar].end(),
137  d_rayleigh_ptrs[lev][Rayleigh::thetabar].begin());
138 
139  if (lev < finest_level) {
140  refine_fac *= ref_ratio[lev][2];
141  }
142  }
143 }
AMREX_FORCE_INLINE void expand_and_interpolate_1d(amrex::Vector< amrex::Real > &znew, const amrex::Vector< amrex::Real > &zorig, int refine_fac, bool destag=false)
Definition: ERF_Interpolation_1D.H:89
amrex::Vector< amrex::Vector< amrex::Real > > theta_inp_sound
Definition: ERF_InputSoundingData.H:409
amrex::Vector< amrex::Vector< amrex::Real > > z_inp_sound
Definition: ERF_InputSoundingData.H:409
amrex::Vector< amrex::Vector< amrex::Real > > U_inp_sound
Definition: ERF_InputSoundingData.H:409
amrex::Vector< amrex::Vector< amrex::Real > > V_inp_sound
Definition: ERF_InputSoundingData.H:409
int size(int itime) const
Definition: ERF_InputSoundingData.H:384
Here is the call graph for this function:

◆ setRecordDataInfo()

void ERF::setRecordDataInfo ( int  i,
const std::string &  filename 
)
inlineprivate
1625  {
1626  if (amrex::ParallelDescriptor::IOProcessor())
1627  {
1628  datalog[i] = std::make_unique<std::fstream>();
1629  datalog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
1630  if (!datalog[i]->good()) {
1631  amrex::FileOpenFailed(filename);
1632  }
1633  }
1634  amrex::ParallelDescriptor::Barrier("ERF::setRecordDataInfo");
1635  }

◆ setRecordDerDataInfo()

void ERF::setRecordDerDataInfo ( int  i,
const std::string &  filename 
)
inlineprivate
1638  {
1639  if (amrex::ParallelDescriptor::IOProcessor())
1640  {
1641  der_datalog[i] = std::make_unique<std::fstream>();
1642  der_datalog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
1643  if (!der_datalog[i]->good()) {
1644  amrex::FileOpenFailed(filename);
1645  }
1646  }
1647  amrex::ParallelDescriptor::Barrier("ERF::setRecordDerDataInfo");
1648  }

◆ setRecordEnergyDataInfo()

void ERF::setRecordEnergyDataInfo ( int  i,
const std::string &  filename 
)
inlineprivate
1651  {
1652  if (amrex::ParallelDescriptor::IOProcessor())
1653  {
1654  tot_e_datalog[i] = std::make_unique<std::fstream>();
1655  tot_e_datalog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
1656  if (!tot_e_datalog[i]->good()) {
1657  amrex::FileOpenFailed(filename);
1658  }
1659  }
1660  amrex::ParallelDescriptor::Barrier("ERF::setRecordEnergyDataInfo");
1661  }

◆ setRecordSampleLineInfo()

void ERF::setRecordSampleLineInfo ( int  i,
int  lev,
amrex::IntVect &  cell,
const std::string &  filename 
)
inlineprivate
1681  {
1682  amrex::MultiFab dummy(grids[lev],dmap[lev],1,0);
1683  for (amrex::MFIter mfi(dummy); mfi.isValid(); ++mfi)
1684  {
1685  const amrex::Box& bx = mfi.validbox();
1686  if (bx.contains(cell)) {
1687  samplelinelog[i] = std::make_unique<std::fstream>();
1688  samplelinelog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
1689  if (!samplelinelog[i]->good()) {
1690  amrex::FileOpenFailed(filename);
1691  }
1692  }
1693  }
1694  amrex::ParallelDescriptor::Barrier("ERF::setRecordSampleLineInfo");
1695  }

◆ setRecordSamplePointInfo()

void ERF::setRecordSamplePointInfo ( int  i,
int  lev,
amrex::IntVect &  cell,
const std::string &  filename 
)
inlineprivate
1664  {
1665  amrex::MultiFab dummy(grids[lev],dmap[lev],1,0);
1666  for (amrex::MFIter mfi(dummy); mfi.isValid(); ++mfi)
1667  {
1668  const amrex::Box& bx = mfi.validbox();
1669  if (bx.contains(cell)) {
1670  sampleptlog[i] = std::make_unique<std::fstream>();
1671  sampleptlog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
1672  if (!sampleptlog[i]->good()) {
1673  amrex::FileOpenFailed(filename);
1674  }
1675  }
1676  }
1677  amrex::ParallelDescriptor::Barrier("ERF::setRecordSamplePointInfo");
1678  }

◆ setSpongeRefFromSounding()

void ERF::setSpongeRefFromSounding ( bool  restarting)
private

Set sponge mean profiles from input sounding.

Sets the sponge damping averaged quantities from an externally supplied input sponge data file.

Parameters
[in]restartingBoolean parameter that indicates whether we are currently restarting from a checkpoint file.
66 {
67  // If we are restarting then we haven't read the input_sponge file yet
68  // so we need to read it here
69  // TODO: should we store this information in the checkpoint file instead?
70  if (restarting) {
72  }
73 
74  const Real* z_inp_sponge = input_sponge_data.z_inp_sponge.dataPtr();
75  const Real* U_inp_sponge = input_sponge_data.U_inp_sponge.dataPtr();
76  const Real* V_inp_sponge = input_sponge_data.V_inp_sponge.dataPtr();
77  const int inp_sponge_size = input_sponge_data.size();
78 
79  for (int lev = 0; lev <= finest_level; lev++)
80  {
81  const int khi = geom[lev].Domain().bigEnd()[2];
82  Vector<Real> zcc(khi+1);
83 
84  if (z_phys_cc[lev]) {
85  // use_terrain=1
86  // calculate the damping strength based on the max height at each k
88  } else {
89  const auto *const prob_lo = geom[lev].ProbLo();
90  const auto *const dx = geom[lev].CellSize();
91  for (int k = 0; k <= khi; k++)
92  {
93  zcc[k] = prob_lo[2] + (k+myhalf) * dx[2];
94  }
95  }
96 
97  for (int k = 0; k <= khi; k++)
98  {
99  h_sponge_ptrs[lev][Sponge::ubar_sponge][k] = interpolate_1d(z_inp_sponge, U_inp_sponge, zcc[k], inp_sponge_size);
100  h_sponge_ptrs[lev][Sponge::vbar_sponge][k] = interpolate_1d(z_inp_sponge, V_inp_sponge, zcc[k], inp_sponge_size);
101  }
102 
103  // Copy from host version to device version
104  Gpu::copy(Gpu::hostToDevice, h_sponge_ptrs[lev][Sponge::ubar_sponge].begin(), h_sponge_ptrs[lev][Sponge::ubar_sponge].end(),
105  d_sponge_ptrs[lev][Sponge::ubar_sponge].begin());
106  Gpu::copy(Gpu::hostToDevice, h_sponge_ptrs[lev][Sponge::vbar_sponge].begin(), h_sponge_ptrs[lev][Sponge::vbar_sponge].end(),
107  d_sponge_ptrs[lev][Sponge::vbar_sponge].begin());
108  }
109 }
AMREX_FORCE_INLINE void reduce_to_max_per_height(amrex::Vector< amrex::Real > &v, std::unique_ptr< amrex::MultiFab > &mf)
Definition: ERF_ParFunctions.H:8
amrex::Vector< amrex::Real > V_inp_sponge
Definition: ERF_InputSpongeData.H:111
amrex::Vector< amrex::Real > z_inp_sponge
Definition: ERF_InputSpongeData.H:111
amrex::Vector< amrex::Real > U_inp_sponge
Definition: ERF_InputSpongeData.H:111
int size() const
Definition: ERF_InputSpongeData.H:99
Here is the call graph for this function:

◆ setSubVolVariables()

void ERF::setSubVolVariables ( const std::string &  pp_subvol_var_names,
amrex::Vector< std::string > &  subvol_var_names 
)
private
11 {
12  ParmParse pp(pp_prefix);
13 
14  std::string nm;
15 
16  int nSubVolVars = pp.countval(pp_subvol_var_names.c_str());
17 
18  // We pre-populate the list with velocities, but allow these to be over-written
19  // by user input
20  if (nSubVolVars == 0)
21  {
22  subvol_var_names.push_back("x_velocity");
23  subvol_var_names.push_back("y_velocity");
24  subvol_var_names.push_back("z_velocity");
25 
26  } else {
27  for (int i = 0; i < nSubVolVars; i++)
28  {
29  pp.get(pp_subvol_var_names.c_str(), nm, i);
30 
31  // Add the named variable to our list of subvol variables
32  // if it is not already in the list
33  if (!containerHasElement(subvol_var_names, nm)) {
34  subvol_var_names.push_back(nm);
35  }
36  }
37  }
38 
39  // Get state variables in the same order as we define them,
40  // since they may be in any order in the input list
41  Vector<std::string> tmp_plot_names;
42 
43  for (int i = 0; i < cons_names.size(); ++i) {
44  if ( containerHasElement(subvol_var_names, cons_names[i]) ) {
45  if (solverChoice.moisture_type == MoistureType::None) {
46  if (cons_names[i] != "rhoQ1" && cons_names[i] != "rhoQ2" && cons_names[i] != "rhoQ3" &&
47  cons_names[i] != "rhoQ4" && cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
48  {
49  tmp_plot_names.push_back(cons_names[i]);
50  }
51  } else if (solverChoice.moisture_type == MoistureType::Kessler) { // allow rhoQ1, rhoQ2, rhoQ3
52  if (cons_names[i] != "rhoQ4" && cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
53  {
54  tmp_plot_names.push_back(cons_names[i]);
55  }
56  } else if ( (solverChoice.moisture_type == MoistureType::SatAdj) ||
57  (solverChoice.moisture_type == MoistureType::SAM_NoPrecip_NoIce) ||
58  (solverChoice.moisture_type == MoistureType::Kessler_NoRain) ) { // allow rhoQ1, rhoQ2
59  if (cons_names[i] != "rhoQ3" && cons_names[i] != "rhoQ4" &&
60  cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
61  {
62  tmp_plot_names.push_back(cons_names[i]);
63  }
64  } else if ( (solverChoice.moisture_type == MoistureType::Morrison_NoIce) ||
65  (solverChoice.moisture_type == MoistureType::SAM_NoIce ) ) { // allow rhoQ1, rhoQ2, rhoQ4
66  if (cons_names[i] != "rhoQ3" && cons_names[i] != "rhoQ5" && cons_names[i] != "rhoQ6")
67  {
68  tmp_plot_names.push_back(cons_names[i]);
69  }
70  } else
71  {
72  // For moisture_type SAM and Morrison we have all six variables
73  tmp_plot_names.push_back(cons_names[i]);
74  }
75  }
76  }
77 
78  // Check for velocity since it's not in cons_names
79  if (containerHasElement(subvol_var_names, "x_velocity")) {
80  tmp_plot_names.push_back("x_velocity");
81  }
82  if (containerHasElement(subvol_var_names, "y_velocity")) {
83  tmp_plot_names.push_back("y_velocity");
84  }
85  if (containerHasElement(subvol_var_names, "z_velocity")) {
86  tmp_plot_names.push_back("z_velocity");
87  }
88 
89  //
90  // If the model we are running doesn't have the variable listed in the inputs file,
91  // just ignore it rather than aborting
92  //
93  for (int i = 0; i < derived_subvol_names.size(); ++i) {
94  if ( containerHasElement(subvol_var_names, derived_names[i]) ) {
95  bool ok_to_add = ( (solverChoice.terrain_type == TerrainType::ImmersedForcing) ||
96  (derived_names[i] != "terrain_IB_mask") );
97  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
98  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
99  (derived_names[i] != "detJ") );
100  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
101  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
102  (derived_names[i] != "z_phys") );
103  if (ok_to_add)
104  {
105  if (solverChoice.moisture_type == MoistureType::None) { // no moist quantities allowed
106  if (derived_names[i] != "qv" && derived_names[i] != "qc" && derived_names[i] != "qrain" &&
107  derived_names[i] != "qi" && derived_names[i] != "qsnow" && derived_names[i] != "qgraup" &&
108  derived_names[i] != "qt" && derived_names[i] != "qn" && derived_names[i] != "qp" &&
109  derived_names[i] != "rain_accum" && derived_names[i] != "snow_accum" && derived_names[i] != "graup_accum")
110  {
111  tmp_plot_names.push_back(derived_names[i]);
112  }
113  } else if ( (solverChoice.moisture_type == MoistureType::Kessler ) ||
114  (solverChoice.moisture_type == MoistureType::Morrison_NoIce) ||
115  (solverChoice.moisture_type == MoistureType::SAM_NoIce ) ) { // allow qv, qc, qrain
116  if (derived_names[i] != "qi" && derived_names[i] != "qsnow" && derived_names[i] != "qgraup" &&
117  derived_names[i] != "snow_accum" && derived_names[i] != "graup_accum")
118  {
119  tmp_plot_names.push_back(derived_names[i]);
120  }
121  } else if ( (solverChoice.moisture_type == MoistureType::SatAdj) ||
122  (solverChoice.moisture_type == MoistureType::SAM_NoPrecip_NoIce) ||
123  (solverChoice.moisture_type == MoistureType::Kessler_NoRain) ) { // allow qv, qc
124  if (derived_names[i] != "qrain" &&
125  derived_names[i] != "qi" && derived_names[i] != "qsnow" && derived_names[i] != "qgraup" &&
126  derived_names[i] != "qp" &&
127  derived_names[i] != "rain_accum" && derived_names[i] != "snow_accum" && derived_names[i] != "graup_accum")
128  {
129  tmp_plot_names.push_back(derived_names[i]);
130  }
131  } else
132  {
133  // For moisture_type SAM and Morrison we have all moist quantities
134  tmp_plot_names.push_back(derived_names[i]);
135  }
136  } // use_terrain?
137  } // hasElement
138  }
139 
140  subvol_var_names = tmp_plot_names;
141 }
const amrex::Vector< std::string > derived_subvol_names
Definition: ERF.H:1267
Here is the call graph for this function:

◆ solve_with_gmres()

void ERF::solve_with_gmres ( int  lev,
const amrex::Box &  subdomain,
amrex::MultiFab &  rhs,
amrex::MultiFab &  p,
amrex::Array< amrex::MultiFab, AMREX_SPACEDIM > &  fluxes,
amrex::MultiFab &  ax_sub,
amrex::MultiFab &  ay_sub,
amrex::MultiFab &  az_sub,
amrex::MultiFab &  ,
amrex::MultiFab &  znd_sub 
)

Solve the Poisson equation using FFT-preconditioned GMRES

16 {
17 #ifdef ERF_USE_FFT
18  BL_PROFILE("ERF::solve_with_gmres()");
19 
22 
23  auto const dom_lo = lbound(Geom(lev).Domain());
24  auto const dom_hi = ubound(Geom(lev).Domain());
25 
26  auto const sub_lo = lbound(subdomain);
27  auto const sub_hi = ubound(subdomain);
28 
29  auto dx = Geom(lev).CellSizeArray();
30 
31  Geometry my_geom;
32 
33  Array<int,AMREX_SPACEDIM> is_per; is_per[0] = 0; is_per[1] = 0; is_per[2] = 0;
34  if (Geom(lev).isPeriodic(0) && sub_lo.x == dom_lo.x && sub_hi.x == dom_hi.x) { is_per[0] = 1;}
35  if (Geom(lev).isPeriodic(1) && sub_lo.y == dom_lo.y && sub_hi.y == dom_hi.y) { is_per[1] = 1;}
36 
37  int coord_sys = 0;
38 
39  // If subdomain == domain then we pass Geom(lev) to the FFT solver
40  if (subdomain == Geom(lev).Domain()) {
41  my_geom.define(Geom(lev).Domain(), Geom(lev).ProbDomain(), coord_sys, is_per);
42  } else {
43  // else we create a new geometry based only on the subdomain
44  // The information in my_geom used by the FFT routines is:
45  // 1) my_geom.Domain()
46  // 2) my_geom.CellSize()
47  // 3) my_geom.isAllPeriodic() / my_geom.periodicity()
48  RealBox rb( sub_lo.x *dx[0], sub_lo.y *dx[1], sub_lo.z *dx[2],
49  (sub_hi.x+1)*dx[0], (sub_hi.y+1)*dx[1], (sub_hi.z+1)*dx[2]);
50  my_geom.define(subdomain, rb, coord_sys, is_per);
51  }
52 
53  amrex::GMRES<MultiFab, TerrainPoisson> gmsolver;
54 
55  TerrainPoisson tp(my_geom, rhs.boxArray(), rhs.DistributionMap(), domain_bc_type,
56  stretched_dz_d[lev], ax_sub, ay_sub, az_sub, dJ_sub, &znd_sub,
58 
59  gmsolver.define(tp);
60 
61  gmsolver.setVerbose(mg_verbose);
62 
63  gmsolver.setRestartLength(50);
64 
65  tp.usePrecond(true);
66 
67  gmsolver.solve(phi, rhs, reltol, abstol);
68 
69  tp.getFluxes(phi, fluxes);
70 
71  for (MFIter mfi(phi); mfi.isValid(); ++mfi)
72  {
73  Box xbx = mfi.nodaltilebox(0);
74  Box ybx = mfi.nodaltilebox(1);
75  const Array4<Real >& fx_ar = fluxes[0].array(mfi);
76  const Array4<Real >& fy_ar = fluxes[1].array(mfi);
77  const Array4<Real const>& mf_ux = mapfac[lev][MapFacType::u_x]->const_array(mfi);
78  const Array4<Real const>& mf_vy = mapfac[lev][MapFacType::v_y]->const_array(mfi);
80  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
81  {
82  fx_ar(i,j,k) *= mf_ux(i,j,0);
83  },
84  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
85  {
86  fy_ar(i,j,k) *= mf_vy(i,j,0);
87  });
88  } // mfi
89 #else
90  amrex::ignore_unused(lev, rhs, phi, fluxes, ax_sub, ay_sub, az_sub, dJ_sub, znd_sub);
91 #endif
92 
93  // ****************************************************************************
94  // Impose bc's on pprime
95  // ****************************************************************************
96  ImposeBCsOnPhi(lev, phi, subdomain);
97 }
void ImposeBCsOnPhi(int lev, amrex::MultiFab &phi, const amrex::Box &subdomain)
Definition: ERF_ImposeBCsOnPhi.cpp:12
Here is the call graph for this function:

◆ sum_derived_quantities()

void ERF::sum_derived_quantities ( double  time)
178 {
179  if (verbose <= 0 || NumDerDataLogs() <= 0) return;
180 
181  int lev = 0;
182 
183  AMREX_ALWAYS_ASSERT(lev == 0);
184 
185  auto& mfx0 = *mapfac[0][MapFacType::m_x];
186  auto& mfy0 = *mapfac[0][MapFacType::m_x];
187  auto& dJ0 = *detJ_cc[0];
188 
189  // ************************************************************************
190  // WARNING: we are not filling ghost cells other than periodic outside the domain
191  // ************************************************************************
192 
193  MultiFab mf_cc_vel(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,1));
194  mf_cc_vel.setVal(0.); // We just do this to avoid uninitialized values
195 
196  // Average all three components of velocity (on faces) to the cell center
197  average_face_to_cellcenter(mf_cc_vel,0,
198  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],
199  &vars_new[lev][Vars::yvel],
200  &vars_new[lev][Vars::zvel]});
201  mf_cc_vel.FillBoundary(geom[lev].periodicity());
202 
203  if (!geom[lev].isPeriodic(0) || !geom[lev].isPeriodic(1) || !geom[lev].isPeriodic(2)) {
204  amrex::Warning("Ghost cells outside non-periodic physical boundaries are not filled -- vel set to 0 there");
205  }
206 
207  MultiFab r_wted_magvelsq(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
208  MultiFab unwted_magvelsq(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
209  MultiFab enstrophysq(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,1));
210  MultiFab theta_mf(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
211 
212 #ifdef _OPENMP
213 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
214 #endif
215  for (MFIter mfi(unwted_magvelsq, TilingIfNotGPU()); mfi.isValid(); ++mfi)
216  {
217  const Box& bx = mfi.tilebox();
218  auto& src_fab = mf_cc_vel[mfi];
219 
220  auto& dest1_fab = unwted_magvelsq[mfi];
221  // NOTE: we send in src_fab where we should
222  derived::erf_dermagvelsq(bx, dest1_fab, 0, 1, src_fab, (*z_phys_cc[lev])[mfi], Geom(lev), t_new[0], nullptr, lev);
223 
224  auto& dest2_fab = enstrophysq[mfi];
225  derived::erf_derenstrophysq(bx, dest2_fab, 0, 1, src_fab, (*z_phys_cc[lev])[mfi], Geom(lev), t_new[0], nullptr, lev);
226  }
227 
228  // Copy the MF holding 1/2(u^2 + v^2 + w^2) into the MF that will hold 1/2 rho (u^2 + v^2 + w^2)d
229  MultiFab::Copy(r_wted_magvelsq, unwted_magvelsq, 0, 0, 1, 0);
230 
231  // Multiply the MF holding 1/2(u^2 + v^2 + w^2) by rho to get 1/2 rho (u^2 + v^2 + w^2)
232  MultiFab::Multiply(r_wted_magvelsq, vars_new[lev][Vars::cons], 0, 0, 1, 0);
233 
234  // Copy the MF holding (rho theta) into "theta_mf"
235  MultiFab::Copy(theta_mf, vars_new[lev][Vars::cons], RhoTheta_comp, 0, 1, 0);
236 
237  // Divide (rho theta) by rho to get theta in the MF "theta_mf"
238  MultiFab::Divide(theta_mf, vars_new[lev][Vars::cons], Rho_comp, 0, 1, 0);
239 
240  Real unwted_avg = volWgtSumMF(lev, unwted_magvelsq, 0, dJ0, mfx0, mfy0, false);
241  Real r_wted_avg = volWgtSumMF(lev, r_wted_magvelsq, 0, dJ0, mfx0, mfy0, false);
242  Real enstrsq_avg = volWgtSumMF(lev, enstrophysq, 0, dJ0, mfx0, mfy0, false);
243  Real theta_avg = volWgtSumMF(lev, theta_mf, 0, dJ0, mfx0, mfy0, false);
244 
245  // Get volume including terrain (consistent with volWgtSumMF routine)
246  MultiFab volume(grids[lev], dmap[lev], 1, 0);
247  auto const& dx = geom[lev].CellSizeArray();
248  Real cell_vol = dx[0]*dx[1]*dx[2];
249  volume.setVal(cell_vol);
250  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
251  MultiFab::Multiply(volume, *detJ_cc[lev], 0, 0, 1, 0);
252  }
253 #ifdef _OPENMP
254 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
255 #endif
256  for (MFIter mfi(volume, TilingIfNotGPU()); mfi.isValid(); ++mfi)
257  {
258  const Box& tbx = mfi.tilebox();
259  auto dst = volume.array(mfi);
260  const auto& mfx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
261  const auto& mfy = mapfac[lev][MapFacType::m_y]->const_array(mfi);
262  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
263  {
264  dst(i,j,k) /= (mfx(i,j,0)*mfy(i,j,0));
265  });
266  }
267  Real vol = volume.sum();
268 
269  unwted_avg /= vol;
270  r_wted_avg /= vol;
271  enstrsq_avg /= vol;
272  theta_avg /= vol;
273 
274  const int nfoo = 4;
275  Real foo[nfoo] = {unwted_avg,r_wted_avg,enstrsq_avg,theta_avg};
276 #ifdef AMREX_LAZY
277  Lazy::QueueReduction([=]() mutable {
278 #endif
279  ParallelDescriptor::ReduceRealSum(
280  foo, nfoo, ParallelDescriptor::IOProcessorNumber());
281 
282  if (ParallelDescriptor::IOProcessor()) {
283  int i = 0;
284  unwted_avg = foo[i++];
285  r_wted_avg = foo[i++];
286  enstrsq_avg = foo[i++];
287  theta_avg = foo[i++];
288 
289  std::ostream& data_log_der = DerDataLog(0);
290 
291  if (time == zero) {
292  data_log_der << std::setw(datwidth) << " time";
293  data_log_der << std::setw(datwidth) << " ke_den";
294  data_log_der << std::setw(datwidth) << " velsq";
295  data_log_der << std::setw(datwidth) << " enstrophy";
296  data_log_der << std::setw(datwidth) << " int_energy";
297  data_log_der << std::endl;
298  }
299  data_log_der << std::setw(datwidth) << std::setprecision(timeprecision) << time;
300  data_log_der << std::setw(datwidth) << std::setprecision(datprecision) << unwted_avg;
301  data_log_der << std::setw(datwidth) << std::setprecision(datprecision) << r_wted_avg;
302  data_log_der << std::setw(datwidth) << std::setprecision(datprecision) << enstrsq_avg;
303  data_log_der << std::setw(datwidth) << std::setprecision(datprecision) << theta_avg;
304  data_log_der << std::endl;
305 
306  } // if IOProcessor
307 #ifdef AMREX_LAZY
308  }
309 #endif
310 }
AMREX_FORCE_INLINE std::ostream & DerDataLog(int i)
Definition: ERF.H:1532
const int timeprecision
Definition: ERF.H:1129
AMREX_FORCE_INLINE int NumDerDataLogs() noexcept
Definition: ERF.H:1546
void erf_dermagvelsq(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:507
void erf_derenstrophysq(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &zcc_fab, const Geometry &geomdata, Real, const int *, const int)
Definition: ERF_Derive.cpp:443
Here is the call graph for this function:

◆ sum_energy_quantities()

void ERF::sum_energy_quantities ( double  time)
314 {
315  if ( (verbose <= 0) || (tot_e_datalog.size() < 1) ) { return; }
316 
317  int lev = 0;
318 
319  auto& mfx0 = *mapfac[0][MapFacType::m_x];
320  auto& mfy0 = *mapfac[0][MapFacType::m_x];
321  auto& dJ0 = *detJ_cc[0];
322 
323  AMREX_ALWAYS_ASSERT(lev == 0);
324 
325  bool local = true;
326 
327  // ************************************************************************
328  // WARNING: we are not filling ghost cells other than periodic outside the domain
329  // ************************************************************************
330 
331  MultiFab mf_cc_vel(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,1));
332  mf_cc_vel.setVal(0.); // We just do this to avoid uninitialized values
333 
334  // Average all three components of velocity (on faces) to the cell center
335  average_face_to_cellcenter(mf_cc_vel,0,
336  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],
337  &vars_new[lev][Vars::yvel],
338  &vars_new[lev][Vars::zvel]});
339  mf_cc_vel.FillBoundary(geom[lev].periodicity());
340 
341  if (!geom[lev].isPeriodic(0) || !geom[lev].isPeriodic(1) || !geom[lev].isPeriodic(2)) {
342  amrex::Warning("Ghost cells outside non-periodic physical boundaries are not filled -- vel set to 0 there");
343  }
344 
345  MultiFab tot_mass (grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
346  MultiFab tot_energy(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
347 
348  auto const& dx = geom[lev].CellSizeArray();
349  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
350 
351 #ifdef _OPENMP
352 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
353 #endif
354  for (MFIter mfi(tot_mass, TilingIfNotGPU()); mfi.isValid(); ++mfi)
355  {
356  const Box& bx = mfi.tilebox();
357 
358  const Array4<Real>& cc_vel_arr = mf_cc_vel.array(mfi);
359  const Array4<Real>& tot_mass_arr = tot_mass.array(mfi);
360  const Array4<Real>& tot_energy_arr = tot_energy.array(mfi);
361  const Array4<const Real>& cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
362  const Array4<const Real>& z_arr = (z_phys_nd[lev]) ? z_phys_nd[lev]->const_array(mfi) :
363  Array4<const Real>{};
364  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
365  {
366  Real Qv = (is_moist) ? cons_arr(i,j,k,RhoQ1_comp) : zero;
367  Real Qc = (is_moist) ? cons_arr(i,j,k,RhoQ2_comp) : zero;
368  Real Qt = Qv + Qc;
369  Real Rhod = cons_arr(i,j,k,Rho_comp);
370  Real Rhot = Rhod * (one + Qt);
371  Real Temp = getTgivenRandRTh(Rhod, cons_arr(i,j,k,RhoTheta_comp), Qv);
372  Real TKE = myhalf * ( cc_vel_arr(i,j,k,0)*cc_vel_arr(i,j,k,0)
373  + cc_vel_arr(i,j,k,1)*cc_vel_arr(i,j,k,1)
374  + cc_vel_arr(i,j,k,2)*cc_vel_arr(i,j,k,2) );
375  Real zval = (z_arr) ? z_arr(i,j,k) : Real(k)*dx[2];
376 
377  Real Cv = Cp_d - R_d;
378  Real Cvv = Cp_v - R_v;
379  Real Cpv = Cp_v;
380 
381  tot_mass_arr(i,j,k) = Rhot;
382  tot_energy_arr(i,j,k) = Rhod * ( (Cv + Cvv*Qv + Cpv*Qc)*Temp - L_v*Qc
383  + (one + Qt)*TKE + (one + Qt)*CONST_GRAV*zval );
384 
385  });
386 
387  }
388 
389  Real tot_mass_avg = volWgtSumMF(lev, tot_mass , 0, dJ0, mfx0, mfy0, false, local);
390  Real tot_energy_avg = volWgtSumMF(lev, tot_energy, 0, dJ0, mfx0, mfy0, false, local);
391 
392  // Get volume including terrain (consistent with volWgtSumMF routine)
393  MultiFab volume(grids[lev], dmap[lev], 1, 0);
394  Real cell_vol = dx[0]*dx[1]*dx[2];
395  volume.setVal(cell_vol);
396  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
397  MultiFab::Multiply(volume, *detJ_cc[lev], 0, 0, 1, 0);
398  }
399 #ifdef _OPENMP
400 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
401 #endif
402  for (MFIter mfi(volume, TilingIfNotGPU()); mfi.isValid(); ++mfi)
403  {
404  const Box& tbx = mfi.tilebox();
405  auto dst = volume.array(mfi);
406  const auto& mfx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
407  const auto& mfy = mapfac[lev][MapFacType::m_y]->const_array(mfi);
408  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
409  {
410  dst(i,j,k) /= (mfx(i,j,0)*mfy(i,j,0));
411  });
412  }
413  Real vol = volume.sum();
414 
415  // Divide by the volume
416  tot_mass_avg /= vol;
417  tot_energy_avg /= vol;
418 
419  const int nfoo = 2;
420  Real foo[nfoo] = {tot_mass_avg,tot_energy_avg};
421 #ifdef AMREX_LAZY
422  Lazy::QueueReduction([=]() mutable {
423 #endif
424  ParallelDescriptor::ReduceRealSum(
425  foo, nfoo, ParallelDescriptor::IOProcessorNumber());
426 
427  if (ParallelDescriptor::IOProcessor()) {
428  int i = 0;
429  tot_mass_avg = foo[i++];
430  tot_energy_avg = foo[i++];
431 
432  std::ostream& data_log_energy = *tot_e_datalog[0];
433 
434  if (time == zero) {
435  data_log_energy << std::setw(datwidth) << " time";
436  data_log_energy << std::setw(datwidth) << " tot_mass";
437  data_log_energy << std::setw(datwidth) << " tot_energy";
438  data_log_energy << std::endl;
439  }
440  data_log_energy << std::setw(datwidth) << std::setprecision(timeprecision) << time;
441  data_log_energy << std::setw(datwidth) << std::setprecision(datprecision) << tot_mass_avg;
442  data_log_energy << std::setw(datwidth) << std::setprecision(datprecision) << tot_energy_avg;
443  data_log_energy << std::endl;
444 
445  } // if IOProcessor
446 #ifdef AMREX_LAZY
447  }
448 #endif
449 }
constexpr amrex::Real R_v
Definition: ERF_Constants.H:43
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:44
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:53
constexpr amrex::Real Cp_v
Definition: ERF_Constants.H:45
constexpr amrex::Real R_d
Definition: ERF_Constants.H:42
constexpr amrex::Real L_v
Definition: ERF_Constants.H:48
Here is the call graph for this function:

◆ sum_integrated_quantities()

void ERF::sum_integrated_quantities ( double  time)

Computes the integrated quantities on the grid such as the total scalar and total mass quantities. Prints and writes to output file.

Parameters
timeCurrent time
16 {
17  BL_PROFILE("ERF::sum_integrated_quantities()");
18 
19  if (verbose <= 0)
20  return;
21 
22  // Single level sum
23  Real mass_sl;
24 
25  // Multilevel sums
26  Real mass_ml = zero;
27  Real rhth_ml = zero;
28  Real scal_ml = zero;
29  Real mois_ml = zero;
30 
31  bool local = true;
32 
33  auto& mfx0 = *mapfac[0][MapFacType::m_x];
34  auto& mfy0 = *mapfac[0][MapFacType::m_x];
35  auto& dJ0 = *detJ_cc[0];
36 
37  mass_sl = volWgtSumMF(0,vars_new[0][Vars::cons],Rho_comp,dJ0,mfx0,mfy0,false,local);
38 
39  for (int lev = 0; lev <= finest_level; lev++) {
40  auto& mfx = *mapfac[lev][MapFacType::m_x];
41  auto& mfy = *mapfac[lev][MapFacType::m_x];
42  auto& dJ = *detJ_cc[lev];
43  mass_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons],Rho_comp,dJ,mfx,mfy,true);
44  }
45 
46  Real rhth_sl = volWgtSumMF(0,vars_new[0][Vars::cons], RhoTheta_comp,dJ0,mfx0,mfy0,false);
47  Real scal_sl = volWgtSumMF(0,vars_new[0][Vars::cons],RhoScalar_comp,dJ0,mfx0,mfy0,false);
48  Real mois_sl = zero;
49  if (solverChoice.moisture_type != MoistureType::None) {
50  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
51  for (int qoff(0); qoff<n_qstate_into_total; ++qoff) {
52  mois_sl += volWgtSumMF(0,vars_new[0][Vars::cons],RhoQ1_comp+qoff,dJ0,mfx0,mfy0,false);
53  }
54  }
55 
56  for (int lev = 0; lev <= finest_level; lev++) {
57  auto& mfx = *mapfac[lev][MapFacType::m_x];
58  auto& mfy = *mapfac[lev][MapFacType::m_x];
59  auto& dJ = *detJ_cc[lev];
60  rhth_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons], RhoTheta_comp,dJ,mfx,mfy,true);
61  scal_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons],RhoScalar_comp,dJ,mfx,mfy,true);
62  if (solverChoice.moisture_type != MoistureType::None) {
63  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
64  for (int qoff(0); qoff<n_qstate_into_total; ++qoff) {
65  mois_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons],RhoQ1_comp+qoff,dJ,mfx,mfy,false);
66  }
67  }
68  }
69 
70  Gpu::HostVector<Real> h_avg_ustar; h_avg_ustar.resize(1);
71  Gpu::HostVector<Real> h_avg_tstar; h_avg_tstar.resize(1);
72  Gpu::HostVector<Real> h_avg_olen; h_avg_olen.resize(1);
73  if ((m_SurfaceLayer != nullptr) && (NumDataLogs() > 0)) {
74  Box domain = geom[0].Domain();
75  int zdir = 2;
76  h_avg_ustar = sumToLine(*m_SurfaceLayer->get_u_star(0),0,1,domain,zdir);
77  h_avg_tstar = sumToLine(*m_SurfaceLayer->get_t_star(0),0,1,domain,zdir);
78  h_avg_olen = sumToLine(*m_SurfaceLayer->get_olen(0) ,0,1,domain,zdir);
79 
80  // Divide by the total number of cells we are averaging over
81  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
82  h_avg_ustar[0] /= area_z;
83  h_avg_tstar[0] /= area_z;
84  h_avg_olen[0] /= area_z;
85 
86  } else {
87  h_avg_ustar[0] = zero;
88  h_avg_tstar[0] = zero;
89  h_avg_olen[0] = zero;
90  }
91 
92  const int nfoo = 8;
93  Real foo[nfoo] = {mass_sl,rhth_sl,scal_sl,mois_sl,mass_ml,rhth_ml,scal_ml,mois_ml};
94 #ifdef AMREX_LAZY
95  Lazy::QueueReduction([=]() mutable {
96 #endif
97  ParallelDescriptor::ReduceRealSum(
98  foo, nfoo, ParallelDescriptor::IOProcessorNumber());
99 
100  if (ParallelDescriptor::IOProcessor()) {
101  int i = 0;
102  mass_sl = foo[i++];
103  rhth_sl = foo[i++];
104  scal_sl = foo[i++];
105  mois_sl = foo[i++];
106  mass_ml = foo[i++];
107  rhth_ml = foo[i++];
108  scal_ml = foo[i++];
109  mois_ml = foo[i++];
110 
111  Print() << '\n';
112  Print() << "TIME= " << std::setw(datwidth) << std::setprecision(timeprecision) << std::left << time << '\n';
113  if (finest_level == 0) {
114 #if 1
115  Print() << " MASS = " << mass_sl << '\n';
116 #else
117  Print() << " PERT MASS = " << mass_sl << '\n';
118 #endif
119  Print() << " RHO THETA = " << rhth_sl << '\n';
120  if (solverChoice.transport_scalar) { Print() << " RHO SCALAR = " << scal_sl << '\n'; }
121  if (solverChoice.moisture_type != MoistureType::None) { Print() << " RHO QTOTAL = " << mois_sl << '\n'; }
122  } else {
123 #if 1
124  Print() << " MASS SL/ML = " << mass_sl << " " << mass_ml << '\n';
125 #else
126  Print() << " PERT MASS SL/ML = " << mass_sl << " " << mass_ml << '\n';
127 #endif
128  Print() << " RHO THETA SL/ML = " << rhth_sl << " " << rhth_ml << '\n';
129  if (solverChoice.transport_scalar) { Print() << " RHO SCALAR SL/ML = " << scal_sl << " " << scal_ml << '\n'; }
130  if (solverChoice.moisture_type != MoistureType::None) { Print() << " RHO QTOTAL SL/ML = " << mois_sl << " " << mois_ml << '\n'; }
131  }
132 
133  // The first data log only holds scalars
134  if (NumDataLogs() > 0)
135  {
136  int n_d = 0;
137  std::ostream& data_log1 = DataLog(n_d);
138  if (data_log1.good()) {
139  if (time == zero) {
140  data_log1 << std::setw(datwidth) << " time";
141  data_log1 << std::setw(datwidth) << " u_star";
142  data_log1 << std::setw(datwidth) << " t_star";
143  data_log1 << std::setw(datwidth) << " olen";
144  data_log1 << std::endl;
145  } // time = 0
146 
147  // Write the quantities at this time
148  data_log1 << std::setw(datwidth) << std::setprecision(timeprecision) << time;
149  data_log1 << std::setw(datwidth) << std::setprecision(datprecision) << h_avg_ustar[0];
150  data_log1 << std::setw(datwidth) << std::setprecision(datprecision) << h_avg_tstar[0];
151  data_log1 << std::setw(datwidth) << std::setprecision(datprecision) << h_avg_olen[0];
152  data_log1 << std::endl;
153  } // if good
154  } // loop over i
155  } // if IOProcessor
156 #ifdef AMREX_LAZY
157  });
158 #endif
159 
160  // This is just an alias for convenience
161  int lev = 0;
162  if (NumSamplePointLogs() > 0 && NumSamplePoints() > 0) {
163  for (int i = 0; i < NumSamplePoints(); ++i)
164  {
165  sample_points(lev, time, SamplePoint(i), vars_new[lev][Vars::cons]);
166  }
167  }
168  if (NumSampleLineLogs() > 0 && NumSampleLines() > 0) {
169  for (int i = 0; i < NumSampleLines(); ++i)
170  {
171  sample_lines(lev, time, SampleLine(i), vars_new[lev][Vars::cons]);
172  }
173  }
174 }
AMREX_FORCE_INLINE int NumSampleLineLogs() noexcept
Definition: ERF.H:1575
AMREX_FORCE_INLINE int NumSamplePointLogs() noexcept
Definition: ERF.H:1561
amrex::IntVect & SampleLine(int i)
Definition: ERF.H:1594
AMREX_FORCE_INLINE int NumSamplePoints() noexcept
Definition: ERF.H:1588
AMREX_FORCE_INLINE int NumSampleLines() noexcept
Definition: ERF.H:1601
amrex::IntVect & SamplePoint(int i)
Definition: ERF.H:1581
void sample_points(int lev, amrex::Real time, amrex::IntVect cell, amrex::MultiFab &mf)
Definition: ERF_WriteScalarProfiles.cpp:528
AMREX_FORCE_INLINE std::ostream & DataLog(int i)
Definition: ERF.H:1525
AMREX_FORCE_INLINE int NumDataLogs() noexcept
Definition: ERF.H:1539
void sample_lines(int lev, amrex::Real time, amrex::IntVect cell, amrex::MultiFab &mf)
Definition: ERF_WriteScalarProfiles.cpp:564
bool transport_scalar
Definition: ERF_DataStruct.H:1336

◆ SurfaceDataInterpolation()

void ERF::SurfaceDataInterpolation ( const int  nlevs,
const amrex::Real  time,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  z_phys_nd,
bool  regrid_forces_file_read 
)
146 {
147 
148  static amrex::Vector<Real> next_read_forecast_time;
149  static amrex::Vector<Real> last_read_forecast_time;
150 
151  const int nlevs = a_z_phys_nd.size();
152 
153  Real hindcast_data_interval = solverChoice.hindcast_data_interval_in_hrs*Real(3600.0);
154 
155  // Initialize static vectors once
156  if (next_read_forecast_time.empty()) {
157  next_read_forecast_time.resize(nlevs, -one);
158  last_read_forecast_time.resize(nlevs, -one);
159  Print() << "Initializing the time vector values here by " << lev << std::endl;
160  }
161 
162  if (next_read_forecast_time[lev] < zero) {
163  int next_multiple = static_cast<int>(time / hindcast_data_interval);
164  next_read_forecast_time[lev] = next_multiple * hindcast_data_interval;
165  last_read_forecast_time[lev] = next_read_forecast_time[lev];
166  }
167 
168  if (time >= next_read_forecast_time[lev] or regrid_forces_file_read) {
169 
170  Print() << "Data reading happening at level " << lev << std::endl;
171 
172  std::string folder = solverChoice.hindcast_surface_data_dir;
173 
174  // Check if folder exists and is a directory
175  if (!fs::exists(folder) || !fs::is_directory(folder)) {
176  throw std::runtime_error("Error: Folder '" + folder + "' does not exist or is not a directory.");
177  }
178 
179  std::vector<std::string> bin_files;
180 
181  for (const auto& entry : fs::directory_iterator(folder)) {
182  if (!entry.is_regular_file()) continue;
183 
184  std::string fname = entry.path().filename().string();
185  if (fname.size() >= 4 && fname.substr(fname.size() - 4) == ".bin") {
186  bin_files.push_back(entry.path().string());
187  }
188  }
189  std::sort(bin_files.begin(), bin_files.end());
190 
191  // Check if no .bin files were found
192  if (bin_files.empty()) {
193  throw std::runtime_error("Error: No .bin files found in folder '" + folder + "'.");
194  }
195 
196  std::string filename1, filename2;
197 
198  int idx1 = static_cast<int>(time / hindcast_data_interval);
199  int idx2 = static_cast<int>(time / hindcast_data_interval)+1;
200  Print() << "Reading surface data " << time << " " << idx1 << " " << idx2 <<" " << bin_files.size() << std::endl;
201 
202  if (idx2 >= static_cast<int>(bin_files.size())) {
203  throw std::runtime_error("Error: Not enough .bin files to cover time " + std::to_string(time));
204  }
205 
206  filename1 = bin_files[idx1];
207  filename2 = bin_files[idx2];
208 
211 
212  // Create the time-interpolated forecast state
213  //CreateForecastStateMultiFabs(forecast_state_interp);
214  if(!regrid_forces_file_read){
215  last_read_forecast_time[lev] = next_read_forecast_time[lev];
216  next_read_forecast_time[lev] += hindcast_data_interval;
217  Print() << "Next forecast time getting updated here " << std::endl;
218  }
219  }
220 
221  Real prev_read_time = last_read_forecast_time[lev];
222  Real alpha1 = one - (time - prev_read_time)/hindcast_data_interval;
223  Real alpha2 = one - alpha1;
224 
225  amrex::Print()<< "The values of alpha1 and alpha2 are " << alpha1 << " "<< alpha2 <<std::endl;
226 
227  if (alpha1 < zero || alpha1 > one ||
228  alpha2 < zero || alpha2 > one)
229  {
230  std::stringstream ss;
231  ss << "Interpolation weights for hindcast files are incorrect: "
232  << "alpha1 = " << alpha1 << ", alpha2 = " << alpha2;
233  Abort(ss.str());
234  }
235 
236  /*MultiFab& mf_surf_interp = surface_state_interp[lev];
237 
238  // Fill the time-interpolated forecast states
239  MultiFab::LinComb(surface_state_interp[lev],
240  alpha1, surface_state_1[lev], 0,
241  alpha2, surface_state_2[lev], 0,
242  0, mf_surf_interp.nComp(), mf_surf_interp.nGrow());
243 
244  std::string pltname = "plt_interp_surface";
245  Vector<std::string> varnames_plot_mf = {"ls_mask", "SST"};
246 
247  const MultiFab& src = vars_new[0][0];
248 
249  MultiFab plot_mf(src.boxArray(),
250  src.DistributionMap(),
251  2, 0);
252 
253  plot_mf.setVal(0.0);
254 
255  for (MFIter mfi(plot_mf); mfi.isValid(); ++mfi) {
256  const Array4<Real> &plot_mf_arr = plot_mf.array(mfi);
257  const Array4<Real> &surf_mf_arr = surface_state_1[0].array(mfi);
258 
259  const Box& bx = mfi.validbox();
260 
261  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
262  plot_mf_arr(i,j,k,0) = surf_mf_arr(i,j,0);
263  plot_mf_arr(i,j,k,1) = surf_mf_arr(i,j,1);
264  });
265  }
266 
267  WriteSingleLevelPlotfile(
268  pltname,
269  plot_mf,
270  varnames_plot_mf,
271  geom[0],
272  time,
273  0 // level
274  );*/
275 }
void FillSurfaceStateMultiFabs(const int lev, const std::string &filename, amrex::Vector< amrex::MultiFab > &surface_state)
Definition: ERF_SurfaceDataInterpolation.cpp:19
std::string hindcast_surface_data_dir
Definition: ERF_DataStruct.H:1435
amrex::Real hindcast_data_interval_in_hrs
Definition: ERF_DataStruct.H:1436

◆ timeStep()

void ERF::timeStep ( int  lev,
double  time,
int  iteration 
)
private

Function that coordinates the evolution across levels – this calls Advance to do the actual advance at this level, then recursively calls itself at finer levels

Parameters
[in]levlevel of refinement (coarsest level is 0)
[in]timestart time for time advance
[in]iterationtime step counter
19 {
20  //
21  // We need to FillPatch the coarse level before assessing whether to regrid
22  // We have not done the swap yet so we fill the "new" which will become the "old"
23  //
24  MultiFab& S_new = vars_new[lev][Vars::cons];
25  MultiFab& U_new = vars_new[lev][Vars::xvel];
26  MultiFab& V_new = vars_new[lev][Vars::yvel];
27  MultiFab& W_new = vars_new[lev][Vars::zvel];
28 
29 #ifdef ERF_USE_NETCDF
30  //
31  // Since we now only read in a subset of the time slices in wrfbdy and
32  // wrflowinp, we need to check whether it's time to read in more.
33  //
34  bool use_moist = (solverChoice.moisture_type != MoistureType::None);
35  if (solverChoice.use_real_bcs && (lev==0))
36  {
37  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp, 1);
38  Array<MultiFab*, AMREX_SPACEDIM> area_vec = {ax[lev].get(), ay[lev].get(), az[lev].get()};
39 
40  int ntimes = bdy_data_xlo.size();
41  double time_since_start_bdy = time + start_time - start_bdy_time;
42  int n_time_old = std::min(static_cast<int>( (time_since_start_bdy ) / bdy_time_interval), ntimes-1);
43  int n_time_new = std::min(static_cast<int>( (time_since_start_bdy+dt[lev]) / bdy_time_interval), ntimes-1);
44 
45  for (int itime = 0; itime < ntimes; itime++)
46  {
47  /*
48  if (bdy_data_xlo[itime].size() > 0) {
49  amrex::Print() << "HAVE BDY DATA AT TIME " << itime << std::endl;
50  } else {
51  amrex::Print() << " NO BDY DATA AT TIME " << itime << std::endl;
52  }
53  */
54 
55  // Note that we never release itime == 0 because it is used for the spatial interpolation at later times
56  bool clear_itime = (itime > 0 && itime < n_time_old);
57 
58  if (clear_itime && bdy_data_xlo[itime].size() > 0) {
59  bdy_data_xlo[itime].clear();
60  bdy_data_xhi[itime].clear();
61  bdy_data_ylo[itime].clear();
62  bdy_data_yhi[itime].clear();
63  //amrex::Print() << "CLEAR BDY DATA AT TIME " << itime << std::endl;
64  }
65 
66  bool need_itime = (itime >= n_time_old && itime <= n_time_new+1);
67  //if (need_itime) { amrex::Print() << "NEED BDY DATA AT TIME " << itime << std::endl; }
68 
69  // Handle erfbdy files (AMReX native format).
70  if (use_erfbdy) {
71  if (bdy_data_xlo[itime].size() == 0 && need_itime) {
73  bdy_data_xlo, bdy_data_xhi,
74  bdy_data_ylo, bdy_data_yhi,
76  }
77  // Handle wrfbdy files (NetCDF format).
78  } else {
79  if (bdy_data_xlo[itime].size() == 0 && need_itime) {
80  bool is_anelastic = (solverChoice.anelastic[0] == 1);
81  read_and_convert_from_wrfbdy(itime,nc_bdy_file,bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
84  r_hse, area_vec, geom[lev], use_moist, domain_bcs_type,
85  real_width, bdy_time_interval, is_anelastic);
86  }
87  } // use_erfbdy
88  } // itime
89  } // use_real_bcs && lev == 0
90 
91  if (!nc_low_file.empty() && (lev==0))
92  {
93  int ntimes = low_data_zlo.size();
94  Real time_since_start_low = time + start_time - start_low_time;
95  int n_time_old = std::min(static_cast<int>( (time_since_start_low ) / low_time_interval), ntimes-1);
96  int n_time_new = std::min(static_cast<int>( (time_since_start_low+dt[lev]) / low_time_interval), ntimes-1);
97 
98  for (int itime = 0; itime < ntimes; itime++)
99  {
100  /*
101  if (low_data_zlo[itime].size() > 0) {
102  amrex::Print() << "HAVE LOW DATA AT TIME " << itime << std::endl;
103  } else {
104  amrex::Print() << " NO LOW DATA AT TIME " << itime << std::endl;
105  }
106  */
107 
108  bool clear_itime = (itime < n_time_old);
109 
110  if (clear_itime && low_data_zlo[itime].size() > 0) {
111  low_data_zlo[itime].clear();
112  //amrex::Print() << "CLEAR LOW DATA AT TIME " << itime << std::endl;
113  }
114 
115  bool need_itime = (itime >= n_time_old && itime <= n_time_new+1);
116  //if (need_itime) { amrex::Print() << "NEED LOW DATA AT TIME " << itime << std::endl; }
117 
118  if (low_data_zlo[itime].size() == 0 && need_itime) {
119  read_from_wrflow(itime, nc_low_file, geom[lev].Domain(), low_data_zlo);
120 
121  update_sst_tsk(itime, geom[lev], ba2d[lev],
122  sst_lev[lev], tsk_lev[lev],
123  m_SurfaceLayer, low_data_zlo,
124  S_new, *mf_PSFC[lev],
125  solverChoice.rdOcp, lmask_lev[lev][0], use_moist);
126  }
127  } // itime
128  } // have nc_low_file && lev == 0
129 #endif
130 
131  //
132  // NOTE: the momenta here are not fillpatched (they are only used as scratch space)
133  //
134  if (lev == 0) {
135  FillPatchCrseLevel(lev, time, {&S_new, &U_new, &V_new, &W_new});
136  } else if (lev < finest_level) {
137  FillPatchFineLevel(lev, time, {&S_new, &U_new, &V_new, &W_new},
138  {&S_new, &rU_new[lev], &rV_new[lev], &rW_new[lev]},
139  base_state[lev], base_state[lev]);
140  }
141 
142  if (regrid_int > 0) // We may need to regrid
143  {
144  // help keep track of whether a level was already regridded
145  // from a coarser level call to regrid
146  static Vector<int> last_regrid_step(max_level+1, 0);
147 
148  // regrid changes level "lev+1" so we don't regrid on max_level
149  // also make sure we don't regrid fine levels again if
150  // it was taken care of during a coarser regrid
151  if (lev < max_level)
152  {
153  if ( (istep[lev] % regrid_int == 0) && (istep[lev] > last_regrid_step[lev]) )
154  {
155  // regrid could add newly refine levels (if finest_level < max_level)
156  // so we save the previous finest level index
157  int old_finest = finest_level;
158 
159  if (solverChoice.coupling_type == CouplingType::TwoWay &&
160  solverChoice.moisture_type != MoistureType::None &&
161  Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian &&
162  finest_level >= 1) {
163  micro->AverageDownMicroVars(finest_level);
164  for (int flev = finest_level-1; flev >= lev; --flev) {
166  }
167  }
168 
169  regrid(lev, time);
170 
171 #ifdef ERF_USE_PARTICLES
172  particleData.Redistribute(z_phys_nd);
173 #endif
174 
175  // mark that we have regridded this level already
176  for (int k = lev; k <= finest_level; ++k) {
177  last_regrid_step[k] = istep[k];
178  }
179 
180  // if there are newly created levels, set the time step
181  for (int k = old_finest+1; k <= finest_level; ++k) {
182  dt[k] = dt[k-1] / static_cast<Real>(nsubsteps[k]);
183  }
184  } // if
185  } // lev
186  }
187 
188  // Update what we call "old" and "new" time
189  t_old[lev] = t_new[lev];
190  t_new[lev] += dt[lev];
191 
192  if (Verbose()) {
193  amrex::Print() << "[Level " << lev << " step " << istep[lev]+1 << "] ";
194  amrex::Print() << std::setprecision(timeprecision)
195  << "ADVANCE from elapsed time = " << t_old[lev] << " to " << t_new[lev]
196  << " with dt = " << dt[lev] << std::endl;
197  }
198 
199 #ifdef ERF_USE_WW3_COUPLING
200  amrex::Print() << " About to call send_to_ww3 from ERF_Timestep" << std::endl;
201  send_to_ww3(lev);
202  amrex::Print() << " About to call read_waves from ERF_Timestep" << std::endl;
203  read_waves(lev);
204  //send_to_ww3(lev);
205  //read_waves(lev);
206  //send_to_ww3(lev);
207 #endif
208 
209  // Advance a single level for a single time step
210  Advance(lev, time, dt[lev], istep[lev], nsubsteps[lev]);
211 
212  ++istep[lev];
213 
214  if (Verbose()) {
215  amrex::Print() << "[Level " << lev << " step " << istep[lev] << "] ";
216  amrex::Print() << "Advanced " << CountCells(lev) << " cells" << std::endl;
217  }
218 
219  if (lev < finest_level)
220  {
221  // recursive call for next-finer level
222  for (int i = 1; i <= nsubsteps[lev+1]; ++i)
223  {
224  Real strt_time_for_fine = time + (i-1)*dt[lev+1];
225  timeStep(lev+1, strt_time_for_fine, i);
226  }
227  }
228 
229  if ( verbose && lev == 0 && solverChoice.moisture_type != MoistureType::None) {
230  amrex::Print() << "Cloud fraction " << time << " " << cloud_fraction(time) << std::endl;
231  }
232 }
void Advance(int lev, amrex::Real time, amrex::Real dt_lev, int iteration, int ncycle)
Definition: ERF_Advance.cpp:20

Referenced by EvolveOneStep().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ turbPert_amplitude()

void ERF::turbPert_amplitude ( const int  lev)
private
34 {
35  // Accessing data
36  auto& lev_new = vars_new[lev];
37 
38  // Creating local data
39  int ncons = lev_new[Vars::cons].nComp();
40  MultiFab cons_data(lev_new[Vars::cons], make_alias, 0, ncons);
41 
42  // Defining BoxArray type
43  auto m_ixtype = cons_data.boxArray().ixType();
44 
45 #ifdef _OPENMP
46 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
47 #endif
48  for (MFIter mfi(lev_new[Vars::cons], TileNoZ()); mfi.isValid(); ++mfi) {
49  const Box &bx = mfi.validbox();
50  const auto &cons_pert_arr = cons_data.array(mfi); // Address of perturbation array
51  const amrex::Array4<const amrex::Real> &pert_cell = turbPert.pb_cell[lev].array(mfi); // per-cell perturbation stored in structure
52 
53  turbPert.apply_tpi(lev, bx, RhoTheta_comp, m_ixtype, cons_pert_arr, pert_cell);
54  } // mfi
55 }
Here is the call graph for this function:

◆ turbPert_update()

void ERF::turbPert_update ( const int  lev,
const amrex::Real  dt 
)
private
13 {
14  // Accessing data
15  auto& lev_new = vars_new[lev];
16 
17  // Create aliases to state data to pass to calc_tpi_update
18  int ncons = lev_new[Vars::cons].nComp();
19  MultiFab cons_data(lev_new[Vars::cons], make_alias, 0, ncons);
20  MultiFab xvel_data(lev_new[Vars::xvel], make_alias, 0, 1);
21  MultiFab yvel_data(lev_new[Vars::yvel], make_alias, 0, 1);
22 
23  // Computing perturbation update time
24  turbPert.calc_tpi_update(lev, local_dt, xvel_data, yvel_data, cons_data);
25 
26  Print() << "Successfully initialized turbulent perturbation update time and amplitude at level "
27  << lev << " with type: " << turbPert.pt_type[lev] << "\n";
28 }
amrex::Vector< int > pt_type
Definition: ERF_TurbPertStruct.H:658

◆ update_box_for_refinement()

void ERF::update_box_for_refinement ( std::string &  ref_prefix,
int &  lev_for_box,
amrex::RealBox &  real_box,
const amrex::Real  time 
)
337 {
338  ParmParse ppr(ref_prefix);
339 
340  Vector<Real> move_start_time, move_stop_time;
341  int ni = ppr.queryarr("move_start_time", move_start_time);
342  int nj = ppr.queryarr("move_stop_time" , move_stop_time);
343  if (ni != nj) {
344  amrex::Print() << "Must be same number of start times as stop times for moving grids" << std::endl;
345  amrex::Abort();
346  }
347  for (int i = 0; i < ni; i++) {
348  if (move_stop_time[i] <= move_start_time[i]) {
349  amrex::Print() << "start time for interval " << i << " is " << move_start_time[i] << std::endl;
350  amrex::Print() << "stop time for interval " << i << " is " << move_stop_time[i] << std::endl;
351  amrex::Abort("moving grid: stop time must be greater than start time");
352  }
353  }
354  for (int i = 1; i < ni; i++) {
355  if (move_start_time[i] < move_stop_time[i-1]) {
356  amrex::Print() << "start time for interval " << i << " is " << move_start_time[i] << std::endl;
357  amrex::Print() << "stop time for interval " << i-1 << " is " << move_stop_time[i-1] << std::endl;
358  amrex::Abort("moving grid: stop time must be less than start time of the next interval");
359  }
360  }
361 
362  Vector<Real> move_speed_x, move_speed_y;
363  int ni2 = ppr.queryarr("move_speed_x", move_speed_x);
364  int nj2 = ppr.queryarr("move_speed_y", move_speed_y);
365  if (ni2 != nj2 ) {
366  amrex::Print() << "Must be same number of speeds in x- and y-directions" << std::endl;
367  amrex::Abort();
368  }
369  if (ni != ni2 ) {
370  amrex::Print() << "Must be same number of speeds as time intervals" << std::endl;
371  amrex::Abort();
372  }
373 
374  Real offset_x = zero;
375  Real offset_y = zero;
376 
377  for (int i = 0; i < ni; i++) {
378  if (time > move_start_time[i]) {
379  offset_x += move_speed_x[i] * (std::min(time,move_stop_time[i]) - move_start_time[i]);
380  offset_y += move_speed_y[i] * (std::min(time,move_stop_time[i]) - move_start_time[i]);
381  }
382  }
383 
384  RealBox orig_real_box;
385  read_box_for_refinement (ref_prefix, lev_for_box, orig_real_box);
386 
387  Real xlo = orig_real_box.lo(0) + offset_x; Real ylo = orig_real_box.lo(1) + offset_y;
388  Real xhi = orig_real_box.hi(0) + offset_x; Real yhi = orig_real_box.hi(1) + offset_y;
389  Real zlo = orig_real_box.lo(2); Real zhi = orig_real_box.hi(2);
390 
391  real_box.setLo(RealVect(xlo,ylo,zlo));
392  real_box.setHi(RealVect(xhi,yhi,zhi));
393 }
@ ni
Definition: ERF_Morrison.H:46

◆ update_diffusive_arrays()

void ERF::update_diffusive_arrays ( int  lev,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
private
528 {
529  // ********************************************************************************************
530  // Diffusive terms
531  // ********************************************************************************************
532  bool l_use_eb = (SolverChoice::terrain_type == TerrainType::EB);
533  bool l_use_terrain = (SolverChoice::terrain_type != TerrainType::None && !l_use_eb);
534  bool l_use_kturb = solverChoice.turbChoice[lev].use_kturb;
535  bool l_use_diff = ( (solverChoice.diffChoice.molec_diff_type != MolecDiffType::None) ||
536  l_use_kturb );
537  bool l_need_SmnSmn = solverChoice.turbChoice[lev].use_keqn;
538  bool l_use_moist = ( solverChoice.moisture_type != MoistureType::None );
539  bool l_rotate = ( solverChoice.use_rotate_surface_flux );
540 
541  bool l_implicit_diff = (solverChoice.vert_implicit_fac[lev][0] > 0 ||
542  solverChoice.vert_implicit_fac[lev][1] > 0 ||
543  solverChoice.vert_implicit_fac[lev][2] > 0);
544 
545  bool l_eb_surface_layer = (l_use_eb && solverChoice.ebChoice.eb_boundary_type == EBBoundaryType::SurfaceLayer);
546 
547  BoxArray ba12 = convert(ba, IntVect(1,1,0));
548  BoxArray ba13 = convert(ba, IntVect(1,0,1));
549  BoxArray ba23 = convert(ba, IntVect(0,1,1));
550 
551  Tau[lev].resize(9);
552  Tau_corr[lev].resize(3);
553 
554  // Always resize Tau_EB structure, even if not used, because other code checks nullptr
555  Tau_EB[lev].resize(2); // tau_eb13 and tau_eb23
556  for (int comp = 0; comp < 2; ++comp) {
557  Tau_EB[lev][comp].resize(3); // xface, yface, zface
558  }
559 
560  if (l_use_diff) {
561  //
562  // NOTE: We require ghost cells in the vertical when allowing grids that don't
563  // cover the entire vertical extent of the domain at this level
564  //
565  Tau[lev][TauType::tau11] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau11]->setVal(zero);
566  Tau[lev][TauType::tau22] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau22]->setVal(zero);
567  Tau[lev][TauType::tau33] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau33]->setVal(zero);
568 
569  Tau[lev][TauType::tau12] = std::make_unique<MultiFab>( ba12, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau12]->setVal(zero);
570  Tau[lev][TauType::tau13] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau13]->setVal(zero);
571  Tau[lev][TauType::tau23] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau23]->setVal(zero);
572  if (l_use_terrain) {
573  Tau[lev][TauType::tau21] = std::make_unique<MultiFab>( ba12, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau21]->setVal(zero);
574  Tau[lev][TauType::tau31] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau31]->setVal(zero);
575  Tau[lev][TauType::tau32] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau32]->setVal(zero);
576  } else if (l_implicit_diff) {
577  Tau[lev][TauType::tau31] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau31]->setVal(zero);
578  Tau[lev][TauType::tau32] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau32]->setVal(zero);
579  } else {
580  Tau[lev][TauType::tau21] = nullptr;
581  Tau[lev][TauType::tau31] = nullptr;
582  Tau[lev][TauType::tau32] = nullptr;
583  }
584 
585  // EB diffusive stresses - allocate for all three staggered grids
586  if (l_eb_surface_layer) {
587  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::xface] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
588  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::yface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
589  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::zface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
590  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::xface]->setVal(0.);
591  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::yface]->setVal(0.);
592  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::zface]->setVal(0.);
593 
594  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::xface] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
595  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::yface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
596  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::zface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
597  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::xface]->setVal(0.);
598  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::yface]->setVal(0.);
599  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::zface]->setVal(0.);
600  } else {
601  for (int comp = 0; comp < 2; ++comp) {
602  for (int grid = 0; grid < 3; ++grid) {
603  Tau_EB[lev][comp][grid] = nullptr;
604  }
605  }
606  }
607 
608  if (l_implicit_diff && solverChoice.implicit_momentum_diffusion)
609  {
610  Tau_corr[lev][0] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); // Tau31
611  Tau_corr[lev][1] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); // Tau32
612  Tau_corr[lev][0]->setVal(zero);
613  Tau_corr[lev][1]->setVal(zero);
614 #ifdef ERF_IMPLICIT_W
615  Tau_corr[lev][2] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); // Tau33
616  Tau_corr[lev][2]->setVal(zero);
617 #else
618  Tau_corr[lev][2] = nullptr;
619 #endif
620  } else {
621  Tau_corr[lev][0] = nullptr;
622  Tau_corr[lev][1] = nullptr;
623  Tau_corr[lev][2] = nullptr;
624  }
625 
626  SFS_hfx1_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
627  SFS_hfx2_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
628  SFS_hfx3_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
629  SFS_diss_lev[lev] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) );
630  SFS_hfx1_lev[lev]->setVal(zero);
631  SFS_hfx2_lev[lev]->setVal(zero);
632  SFS_hfx3_lev[lev]->setVal(zero);
633  SFS_diss_lev[lev]->setVal(zero);
634 
635  // EB heat fluxes
636  if (l_use_eb) {
637  hfx3_EB[lev] = std::make_unique<MultiFab>( ba, dm, 1, IntVect(1,1,1) );
638  hfx3_EB[lev]->setVal(zero);
639  } else {
640  hfx3_EB[lev] = nullptr;
641  }
642 
643  if (l_use_moist) {
644  SFS_q1fx3_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
645  SFS_q2fx3_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
646  SFS_q1fx3_lev[lev]->setVal(zero);
647  SFS_q2fx3_lev[lev]->setVal(zero);
648  if (l_rotate) {
649  SFS_q1fx1_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
650  SFS_q1fx2_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
651  SFS_q1fx1_lev[lev]->setVal(zero);
652  SFS_q1fx2_lev[lev]->setVal(zero);
653  } else {
654  SFS_q1fx1_lev[lev] = nullptr;
655  SFS_q1fx2_lev[lev] = nullptr;
656  }
657  } else {
658  SFS_q1fx1_lev[lev] = nullptr;
659  SFS_q1fx2_lev[lev] = nullptr;
660  SFS_q1fx3_lev[lev] = nullptr;
661  SFS_q2fx3_lev[lev] = nullptr;
662  }
663  } else {
664  for (int i = 0; i < 9; i++) {
665  Tau[lev][i] = nullptr;
666  }
667  SFS_hfx1_lev[lev] = nullptr; SFS_hfx2_lev[lev] = nullptr; SFS_hfx3_lev[lev] = nullptr;
668  SFS_diss_lev[lev] = nullptr;
669  }
670 
671  if (l_use_kturb) {
672  eddyDiffs_lev[lev] = std::make_unique<MultiFab>(ba, dm, EddyDiff::NumDiffs, 2);
673  eddyDiffs_lev[lev]->setVal(zero);
674  if(l_need_SmnSmn) {
675  SmnSmn_lev[lev] = std::make_unique<MultiFab>( ba, dm, 1, 0 );
676  } else {
677  SmnSmn_lev[lev] = nullptr;
678  }
679  } else {
680  eddyDiffs_lev[lev] = nullptr;
681  SmnSmn_lev[lev] = nullptr;
682  }
683 }
@ tau_eb23
Definition: ERF_EBStruct.H:16
@ tau_eb13
Definition: ERF_EBStruct.H:16
@ yface
Definition: ERF_EBStruct.H:20
@ zface
Definition: ERF_EBStruct.H:20
@ xface
Definition: ERF_EBStruct.H:20
@ NumDiffs
Definition: ERF_IndexDefines.H:215
EBBoundaryType eb_boundary_type
Definition: ERF_EBStruct.H:59
EBChoice ebChoice
Definition: ERF_DataStruct.H:1224

◆ update_terrain_arrays()

void ERF::update_terrain_arrays ( int  lev)
862 {
863  if (SolverChoice::mesh_type == MeshType::StretchedDz ||
864  SolverChoice::mesh_type == MeshType::VariableDz) {
865  make_J(geom[lev],*z_phys_nd[lev],*detJ_cc[lev]);
866  make_areas(geom[lev],*z_phys_nd[lev],*ax[lev],*ay[lev],*az[lev]);
867  make_zcc(geom[lev],*z_phys_nd[lev],*z_phys_cc[lev]);
868  } else { // MeshType::ConstantDz
869  if (SolverChoice::terrain_type == TerrainType::EB) {
870  const auto& ebfact = *eb[lev]->get_const_factory();
871  const MultiFab& volfrac = ebfact.getVolFrac();
872  detJ_cc[lev] = std::make_unique<MultiFab>(volfrac, amrex::make_alias, 0, volfrac.nComp());
873  }
874  }
875 }
void make_areas(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &ax, MultiFab &ay, MultiFab &az)
Definition: ERF_TerrainMetrics.cpp:561
void make_J(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &detJ_cc)
Definition: ERF_TerrainMetrics.cpp:523
Here is the call graph for this function:

◆ volWgtColumnSum()

void ERF::volWgtColumnSum ( int  lev,
const amrex::MultiFab &  mf,
int  comp,
amrex::MultiFab &  mf_2d,
const amrex::MultiFab &  dJ 
)
84 {
85  BL_PROFILE("ERF::volWgtSumColumnMF()");
86 
87  mf_2d.setVal(0.);
88 
89  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
90  // m is the map scale factor at cell centers
91 #ifdef _OPENMP
92 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
93 #endif
94  for (MFIter mfi(mf_to_be_summed, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
95  const Box& bx = mfi.tilebox();
96  const auto dst_arr = mf_2d.array(mfi);
97  const auto src_arr = mf_to_be_summed.array(mfi);
98  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
99  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
100  {
101  amrex::HostDevice::Atomic::Add(&dst_arr(i,j,0),src_arr(i,j,k,comp));
102  });
103  } else {
104  const auto& dJ_arr = dJ.const_array(mfi);
105  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
106  {
107  amrex::HostDevice::Atomic::Add(&dst_arr(i,j,0),src_arr(i,j,k,comp)*dJ_arr(i,j,k));
108  });
109  }
110  } // mfi
111 
112  auto const& dx = geom[lev].CellSizeArray();
113 
114  mf_2d.mult(dx[2]);
115 }
Here is the call graph for this function:

◆ volWgtSumMF()

Real ERF::volWgtSumMF ( int  lev,
const amrex::MultiFab &  mf,
int  comp,
const amrex::MultiFab &  dJ,
const amrex::MultiFab &  mfx,
const amrex::MultiFab &  mfy,
bool  finemask,
bool  local = true 
)

Utility function for computing a volume weighted sum of MultiFab data for a single component

Parameters
levCurrent level
mf_to_be_summed: MultiFab on which we do the volume weighted sum
dJ: volume weighting due to metric terms
mfmx: map factor in x-direction at cell centers
mfmy: map factor in y-direction at cell centers
comp: Index of the component we want to sum
finemask: If a finer level is available, determines whether we mask fine data
local: Boolean sets whether or not to reduce the sum over the domain (false) or compute sums local to each MPI rank (true)
25 {
26  BL_PROFILE("ERF::volWgtSumMF()");
27 
28  Real sum = zero;
29  MultiFab tmp(mf_to_be_summed.boxArray(), mf_to_be_summed.DistributionMap(), 1, 0);
30 
31  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
32  // m is the map scale factor at cell centers
33 #ifdef _OPENMP
34 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
35 #endif
36  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
37  const Box& bx = mfi.tilebox();
38  const auto dst_arr = tmp.array(mfi);
39  const auto src_arr = mf_to_be_summed.array(mfi);
40  const auto& mfx_arr = mfmx.const_array(mfi);
41  const auto& mfy_arr = mfmy.const_array(mfi);
42 
43  if (SolverChoice::terrain_type != TerrainType::EB) {
44  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
45  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
46  {
47  dst_arr(i,j,k,0) = src_arr(i,j,k,comp) / (mfx_arr(i,j,0)*mfy_arr(i,j,0));
48  });
49  } else {
50  const auto& dJ_arr = dJ.const_array(mfi);
51  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
52  {
53  dst_arr(i,j,k,0) = src_arr(i,j,k,comp) * dJ_arr(i,j,k) / (mfx_arr(i,j,0)*mfy_arr(i,j,0));
54  });
55  }
56  } else {
57  const auto& dJ_arr = dJ.const_array(mfi);
58  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
59  {
60  dst_arr(i,j,k,0) = src_arr(i,j,k,comp) * dJ_arr(i,j,k);
61  });
62  }
63 
64  } // mfi
65 
66  if (lev < finest_level && finemask) {
67  MultiFab::Multiply(tmp, *fine_mask[lev+1].get(), 0, 0, 1, 0);
68  }
69 
70  // If local = true then "sum" will be the sum only over the FABs on each rank
71  // If local = false then "sum" will be the sum over the whole MultiFab, and will be broadcast to all ranks
72  sum = tmp.sum(0,local);
73 
74  auto const& dx = geom[lev].CellSizeArray();
75 
76  sum *= dx[0]*dx[1]*dx[2];
77 
78  return sum;
79 }
Here is the call graph for this function:

◆ WeatherDataInterpolation()

void ERF::WeatherDataInterpolation ( const int  nlevs,
const amrex::Real  time,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  z_phys_nd,
bool  regrid_forces_file_read 
)
351 {
352 
353  static amrex::Vector<Real> next_read_forecast_time;
354  static amrex::Vector<Real> last_read_forecast_time;
355 
356  const int nlevs = static_cast<int>(a_z_phys_nd.size());
357 
358  Real hindcast_data_interval = solverChoice.hindcast_data_interval_in_hrs*Real(3600.0);
359 
360  // Initialize static vectors once
361  if (next_read_forecast_time.empty()) {
362  next_read_forecast_time.resize(nlevs, -one);
363  last_read_forecast_time.resize(nlevs, -one);
364  Print() << "Initializing the time vector values here by " << lev << std::endl;
365  }
366 
367  if (next_read_forecast_time[lev] < zero) {
368  int next_multiple = static_cast<int>(time / hindcast_data_interval);
369  next_read_forecast_time[lev] = next_multiple * hindcast_data_interval;
370  last_read_forecast_time[lev] = next_read_forecast_time[lev];
371  }
372 
373  if (time >= next_read_forecast_time[lev] or regrid_forces_file_read) {
374 
375  Print() << "Data reading happening at level " << lev << std::endl;
376 
377  std::string folder = solverChoice.hindcast_boundary_data_dir;
378 
379  // Check if folder exists and is a directory
380  if (!fs::exists(folder) || !fs::is_directory(folder)) {
381  throw std::runtime_error("Error: Folder '" + folder + "' does not exist or is not a directory.");
382  }
383 
384  std::vector<std::string> bin_files;
385 
386  for (const auto& entry : fs::directory_iterator(folder)) {
387  if (!entry.is_regular_file()) continue;
388 
389  std::string fname = entry.path().filename().string();
390  if (fname.size() >= 4 && fname.substr(fname.size() - 4) == ".bin") {
391  bin_files.push_back(entry.path().string());
392  }
393  }
394  std::sort(bin_files.begin(), bin_files.end());
395 
396  // Check if no .bin files were found
397  if (bin_files.empty()) {
398  throw std::runtime_error("Error: No .bin files found in folder '" + folder + "'.");
399  }
400 
401  std::string filename1, filename2;
402 
403  int idx1 = static_cast<int>(time / hindcast_data_interval);
404  int idx2 = static_cast<int>(time / hindcast_data_interval)+1;
405  Print() << "Reading weather data " << time << " " << idx1 << " " << idx2 <<" " << bin_files.size() << std::endl;
406 
407  if (idx2 >= static_cast<int>(bin_files.size())) {
408  throw std::runtime_error("Error: Not enough .bin files to cover time " + std::to_string(time));
409  }
410 
411  filename1 = bin_files[idx1];
412  filename2 = bin_files[idx2];
413 
414  FillForecastStateMultiFabs(lev, filename1, a_z_phys_nd[lev], forecast_state_1);
415  FillForecastStateMultiFabs(lev, filename2, a_z_phys_nd[lev], forecast_state_2);
416 
417  // Create the time-interpolated forecast state
418  //CreateForecastStateMultiFabs(forecast_state_interp);
419  if(!regrid_forces_file_read){
420  last_read_forecast_time[lev] = next_read_forecast_time[lev];
421  next_read_forecast_time[lev] += hindcast_data_interval;
422  Print() << "Next forecast time getting updated here " << std::endl;
423  }
424  }
425 
426  Real prev_read_time = last_read_forecast_time[lev];
427  Real alpha1 = one - (time - prev_read_time)/hindcast_data_interval;
428  Real alpha2 = one - alpha1;
429 
430  amrex::Print()<< "The values of alpha1 and alpha2 are " << alpha1 << " "<< alpha2 <<std::endl;
431 
432  if (alpha1 < zero || alpha1 > one ||
433  alpha2 < zero || alpha2 > one)
434  {
435  std::stringstream ss;
436  ss << "Interpolation weights for hindcast files are incorrect: "
437  << "alpha1 = " << alpha1 << ", alpha2 = " << alpha2;
438  Abort(ss.str());
439  }
440 
441  MultiFab& erf_mf_cons = forecast_state_interp[lev][Vars::cons];
442  MultiFab& erf_mf_xvel = forecast_state_interp[lev][Vars::xvel];
443  MultiFab& erf_mf_yvel = forecast_state_interp[lev][Vars::yvel];
444  //MultiFab& erf_mf_zvel = forecast_state_interp[0][Vars::zvel];
445  MultiFab& erf_mf_latlon = forecast_state_interp[lev][4];
446 
447  // Fill the time-interpolated forecast states
448  MultiFab::LinComb(forecast_state_interp[lev][Vars::cons],
449  alpha1, forecast_state_1[lev][Vars::cons], 0,
450  alpha2, forecast_state_2[lev][Vars::cons], 0,
451  0, erf_mf_cons.nComp(), forecast_state_interp[lev][Vars::cons].nGrow());
452  MultiFab::LinComb(forecast_state_interp[lev][Vars::xvel],
453  alpha1, forecast_state_1[lev][Vars::xvel], 0,
454  alpha2, forecast_state_2[lev][Vars::xvel], 0,
455  0, erf_mf_xvel.nComp(), forecast_state_interp[lev][Vars::xvel].nGrow());
456  MultiFab::LinComb(forecast_state_interp[lev][Vars::yvel],
457  alpha1, forecast_state_1[lev][Vars::yvel], 0,
458  alpha2, forecast_state_2[lev][Vars::yvel], 0,
459  0, erf_mf_yvel.nComp(), forecast_state_interp[lev][Vars::yvel].nGrow());
460  MultiFab::LinComb(forecast_state_interp[lev][4],
461  alpha1, forecast_state_1[lev][4], 0,
462  alpha2, forecast_state_2[lev][4], 0,
463  0, erf_mf_latlon.nComp(), forecast_state_interp[lev][4].nGrow());
464 
465  /*Vector<std::string> varnames_plot_mf = {
466  "rho", "rhotheta", "rhoqv", "rhoqc", "rhoqr", "xvel", "yvel", "zvel", "latitude", "longitude"
467  }; // Customize variable names
468 
469  std::string pltname = "plt_interp";
470 
471  MultiFab plot_mf(erf_mf_cons.boxArray(), erf_mf_cons.DistributionMap(),
472  10, 0);
473 
474  plot_mf.setVal(0.0);
475 
476  for (MFIter mfi(plot_mf); mfi.isValid(); ++mfi) {
477  const Array4<Real> &plot_mf_arr = plot_mf.array(mfi);
478  const Array4<Real> &erf_mf_cons_arr = erf_mf_cons.array(mfi);
479  const Array4<Real> &erf_mf_xvel_arr = erf_mf_xvel.array(mfi);
480  const Array4<Real> &erf_mf_yvel_arr = erf_mf_yvel.array(mfi);
481  const Array4<Real> &erf_mf_zvel_arr = erf_mf_zvel.array(mfi);
482  const Array4<Real> &erf_mf_latlon_arr = erf_mf_latlon.array(mfi);
483 
484  const Box& bx = mfi.validbox();
485 
486  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
487  plot_mf_arr(i,j,k,0) = erf_mf_cons_arr(i,j,k,Rho_comp);
488  plot_mf_arr(i,j,k,1) = erf_mf_cons_arr(i,j,k,RhoTheta_comp);
489  plot_mf_arr(i,j,k,2) = erf_mf_cons_arr(i,j,k,RhoQ1_comp);
490  plot_mf_arr(i,j,k,3) = erf_mf_cons_arr(i,j,k,RhoQ2_comp);
491  plot_mf_arr(i,j,k,4) = erf_mf_cons_arr(i,j,k,RhoQ3_comp);
492 
493  plot_mf_arr(i,j,k,5) = (erf_mf_xvel_arr(i,j,k,0) + erf_mf_xvel_arr(i+1,j,k,0))/two;
494  plot_mf_arr(i,j,k,6) = (erf_mf_yvel_arr(i,j,k,0) + erf_mf_yvel_arr(i,j+1,k,0))/two;
495  plot_mf_arr(i,j,k,7) = (erf_mf_zvel_arr(i,j,k,0) + erf_mf_zvel_arr(i,j,k+1,0))/two;
496 
497  plot_mf_arr(i,j,k,8) = erf_mf_latlon_arr(i,j,k,0);
498  plot_mf_arr(i,j,k,9) = erf_mf_latlon_arr(i,j,k,1);
499  });
500  }
501 
502 
503  WriteSingleLevelPlotfile(
504  pltname,
505  plot_mf,
506  varnames_plot_mf,
507  geom[0],
508  time,
509  0 // level
510  );*/
511 }
void FillForecastStateMultiFabs(const int lev, const std::string &filename, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, amrex::Vector< amrex::Vector< amrex::MultiFab >> &forecast_state)
Definition: ERF_WeatherDataInterpolation.cpp:64
std::string hindcast_boundary_data_dir
Definition: ERF_DataStruct.H:1435

◆ Write2DPlotFile()

void ERF::Write2DPlotFile ( int  which,
PlotFileType  plotfile_type,
amrex::Vector< std::string >  plot_var_names 
)
131 {
132  const int plot_step = istep[0];
133  const std::string plotfilename = make_2d_plotfile_name(which, plot_step,
136 
137  const Vector<std::string> varnames = PlotFileVarNames(plot_var_names);
138  const int ncomp_mf = static_cast<int>(varnames.size());
139 
140  if (ncomp_mf == 0) return;
141 
142  // Vector of MultiFabs for cell-centered data
143  Vector<MultiFab> mf(finest_level+1);
144  for (int lev = 0; lev <= finest_level; ++lev) {
145  mf[lev].define(ba2d[lev], dmap[lev], ncomp_mf, 0);
146  }
147 
148 
149  // **********************************************************************************************
150  // (Effectively) 2D arrays
151  // **********************************************************************************************
152  for (int lev = 0; lev <= finest_level; ++lev)
153  {
154  // Make sure getPgivenRTh and getTgivenRandRTh don't fail
155  if (check_for_nans) {
157  }
158 
159  int mf_comp = 0;
160 
161  // Set all components to zero in case they aren't defined below
162  mf[lev].setVal(0.0);
163 
164  // Expose domain khi and klo at each level
165  int klo = geom[lev].Domain().smallEnd(2);
166  int khi = geom[lev].Domain().bigEnd(2);
167 
168  const MultiFab* pblh_source = nullptr;
169  const MultiFab* sens_flux_source = SFS_hfx3_lev[lev].get();
170  const MultiFab* laten_flux_source = SFS_q1fx3_lev[lev].get();
171  const MultiFab* shoc_ustar_source = nullptr;
172  const MultiFab* shoc_olen_source = nullptr;
173  const MultiFab* shoc_wthv_source = nullptr;
174 #ifdef ERF_USE_NATIVE_SHOC
175  const ShocDriver* native_shoc = native_shoc_driver[lev].get();
176  const bool native_shoc_owns_scalar_fluxes =
177  native_shoc && native_shoc->owns_scalar_surface_fluxes();
178  const bool native_shoc_has_consumed_flux_diagnostics =
179  native_shoc && native_shoc->has_consumed_surface_flux_diagnostics();
180  if (native_shoc && native_shoc->has_native_diagnostics()) {
181  pblh_source = &native_shoc->pblh_diagnostics();
182  }
183  // Native SHOC state_update clears the host SFS arrays after consuming
184  // them. Use SHOC's preserved snapshots only for flux components whose
185  // corresponding host SFS field existed; otherwise keep the source null
186  // so the 2D writer emits the documented -999 missing value.
188  native_shoc_owns_scalar_fluxes,
189  native_shoc_has_consumed_flux_diagnostics,
190  SFS_hfx3_lev[lev] != nullptr)) {
191  sens_flux_source = &native_shoc->consumed_sens_flux_diagnostics();
192  }
194  native_shoc_owns_scalar_fluxes,
195  native_shoc_has_consumed_flux_diagnostics,
196  SFS_q1fx3_lev[lev] != nullptr)) {
197  laten_flux_source = &native_shoc->consumed_laten_flux_diagnostics();
198  }
199  if (native_shoc && native_shoc->has_native_diagnostics()) {
200  shoc_ustar_source = &native_shoc->shoc_ustar_diagnostics();
201  shoc_olen_source = &native_shoc->shoc_olen_diagnostics();
202  shoc_wthv_source = &native_shoc->wthv_sec_diagnostics();
203  }
204 #endif
205  // pblh should follow the active PBL diagnostic provider. Native SHOC
206  // diagnoses its own PBL height in state_update mode; SurfaceLayer
207  // remains the fallback for non-SHOC configurations.
208  if (!pblh_source && m_SurfaceLayer) {
209  pblh_source = m_SurfaceLayer->get_pblh(lev);
210  }
211 
212  if (containerHasElement(plot_var_names, "z_surf")) {
213 #ifdef _OPENMP
214 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
215 #endif
216  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
217  {
218  const Box& bx = mfi.tilebox();
219  const Array4<Real>& derdat = mf[lev].array(mfi);
220  const Array4<const Real>& z_phys_arr = z_phys_nd[lev]->const_array(mfi);
221  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
222  derdat(i, j, k, mf_comp) = Compute_Z_AtWFace(i, j, 0, z_phys_arr);
223  });
224  }
225  mf_comp++;
226  }
227 
228  if (containerHasElement(plot_var_names, "landmask")) {
229 #ifdef _OPENMP
230 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
231 #endif
232  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
233  {
234  const Box& bx = mfi.tilebox();
235  const Array4<Real>& derdat = mf[lev].array(mfi);
236  const Array4<const int>& lmask_arr = lmask_lev[lev][0]->const_array(mfi);
237  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
238  derdat(i, j, k, mf_comp) = static_cast<Real>(lmask_arr(i, j, 0));
239  });
240  }
241  mf_comp++;
242  }
243 
244  if (containerHasElement(plot_var_names, "mapfac")) {
245 #ifdef _OPENMP
246 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
247 #endif
248  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
249  {
250  const Box& bx = mfi.tilebox();
251  const Array4<Real>& derdat = mf[lev].array(mfi);
252  const Array4<Real>& mf_m = mapfac[lev][MapFacType::m_x]->array(mfi);
253  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
254  derdat(i ,j ,k, mf_comp) = mf_m(i,j,0);
255  });
256  }
257  mf_comp++;
258  }
259 
260  if (containerHasElement(plot_var_names, "lat_m")) {
261  if (lat_m[lev]) {
262 #ifdef _OPENMP
263 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
264 #endif
265  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
266  {
267  const Box& bx = mfi.tilebox();
268  const Array4<Real>& derdat = mf[lev].array(mfi);
269  const Array4<Real>& data = lat_m[lev]->array(mfi);
270  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
271  derdat(i, j, k, mf_comp) = data(i,j,0);
272  });
273  }
274  }
275  mf_comp++;
276  } // lat_m
277 
278  if (containerHasElement(plot_var_names, "lon_m")) {
279  if (lon_m[lev]) {
280 #ifdef _OPENMP
281 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
282 #endif
283  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
284  {
285  const Box& bx = mfi.tilebox();
286  const Array4<Real>& derdat = mf[lev].array(mfi);
287  const Array4<Real>& data = lon_m[lev]->array(mfi);
288  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
289  derdat(i, j, k, mf_comp) = data(i,j,0);
290  });
291  }
292  } else {
293  mf[lev].setVal(0.0,mf_comp,1,0);
294  }
295 
296  mf_comp++;
297 
298  } // lon_m
299 
300  ///////////////////////////////////////////////////////////////////////
301  // Surface and single-level diagnostics use the fill helpers below.
302  // Column reductions and future interpolated surfaces keep explicit
303  // assembly paths until their contracts are isolated in dedicated
304  // helpers.
305  if (containerHasElement(plot_var_names, "u_star")) {
307  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_u_star(lev) : nullptr,
308  0, -999);
309  mf_comp++;
310  } // u_star
311 
312  if (containerHasElement(plot_var_names, "w_star")) {
314  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_w_star(lev) : nullptr,
315  0, -999);
316  mf_comp++;
317  } // w_star
318 
319  if (containerHasElement(plot_var_names, "t_star")) {
321  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_t_star(lev) : nullptr,
322  0, -999);
323  mf_comp++;
324  } // t_star
325 
326  if (containerHasElement(plot_var_names, "q_star")) {
328  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_q_star(lev) : nullptr,
329  0, -999);
330  mf_comp++;
331  } // q_star
332 
333  if (containerHasElement(plot_var_names, "Olen")) {
335  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_olen(lev) : nullptr,
336  0, -999);
337  mf_comp++;
338  } // Olen
339 
340  if (containerHasElement(plot_var_names, "pblh")) {
342  mf[lev], mf_comp, pblh_source, 0, -999);
343  mf_comp++;
344  } // pblh
345 
346  if (containerHasElement(plot_var_names, "t_surf")) {
348  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_t_surf(lev) : nullptr,
349  0, -999);
350  mf_comp++;
351  } // t_surf
352 
353  if (containerHasElement(plot_var_names, "q_surf")) {
355  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_q_surf(lev) : nullptr,
356  0, -999);
357  mf_comp++;
358  } // q_surf
359 
360  if (containerHasElement(plot_var_names, "z0")) {
362  mf[lev], mf_comp, m_SurfaceLayer ? m_SurfaceLayer->get_z0(lev) : nullptr,
363  0, -999);
364  mf_comp++;
365  } // z0
366 
367  if (containerHasElement(plot_var_names, "OLR")) {
369  mf[lev], mf_comp, rad_fluxes[lev].get(), khi, -999, 2);
370  mf_comp++;
371  } // OLR
372 
373  if (containerHasElement(plot_var_names, "sens_flux")) {
375  mf[lev], mf_comp, sens_flux_source, klo, -999);
376  mf_comp++;
377  } // sens_flux
378 
379  // Keep the legacy output name "laten_flux"; it maps to the vertical
380  // water-vapor surface flux field.
381  if (containerHasElement(plot_var_names, "laten_flux")) {
383  mf[lev], mf_comp, laten_flux_source, klo, -999);
384  mf_comp++;
385  } // laten_flux
386 
387  if (containerHasElement(plot_var_names, "surf_pres")) {
388  bool moist = (solverChoice.moisture_type != MoistureType::None);
389 #ifdef _OPENMP
390 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
391 #endif
392  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
393  {
394  const Box& bx = mfi.tilebox();
395  const auto& derdat = mf[lev].array(mfi);
396  const auto& cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
397  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
398  auto rt = cons_arr(i,j,klo,RhoTheta_comp);
399  auto qv = (moist) ? cons_arr(i,j,klo,RhoQ1_comp)/cons_arr(i,j,klo,Rho_comp)
400  : zero;
401  derdat(i, j, k, mf_comp) = getPgivenRTh(rt, qv);
402  });
403  }
404  mf_comp++;
405  } // surf_pres
406 
407  if (containerHasElement(plot_var_names, "integrated_qv")) {
408  // Current column-reduction example. Future LWP/IWP diagnostics
409  // should follow a separate column-reduction helper path rather
410  // than the single-k copy helpers.
411  MultiFab mf_qv_int(mf[lev],make_alias,mf_comp,1);
412  if (solverChoice.moisture_type != MoistureType::None) {
413  volWgtColumnSum(lev, vars_new[lev][Vars::cons], RhoQ1_comp, mf_qv_int, *detJ_cc[lev]);
414  } else {
415  mf_qv_int.setVal(0.);
416  }
417  mf_comp++;
418  }
419 
420  if (containerHasElement(plot_var_names, "surface_diagnostic_source")) {
422  mf[lev], mf_comp,
423  m_SurfaceLayer ? m_SurfaceLayer->get_surface_diagnostic_source(lev) : nullptr,
424  0, -999);
425  mf_comp++;
426  } // surface_diagnostic_source
427 
428  if (containerHasElement(plot_var_names, "sensible_heat_flux")) {
430  mf[lev], mf_comp, sens_flux_source, klo, -999);
431  mf_comp++;
432  } // sensible_heat_flux
433 
434  if (containerHasElement(plot_var_names, "latent_heat_flux")) {
436  mf[lev], mf_comp, laten_flux_source, klo, -999);
437  mf_comp++;
438  } // latent_heat_flux
439 
440  if (containerHasElement(plot_var_names, "shoc_u_star")) {
442  mf[lev], mf_comp, shoc_ustar_source, klo, -999);
443  mf_comp++;
444  } // shoc_u_star
445 
446  if (containerHasElement(plot_var_names, "shoc_Olen")) {
448  mf[lev], mf_comp, shoc_olen_source, klo, -999);
449  mf_comp++;
450  } // shoc_Olen
451 
452  if (containerHasElement(plot_var_names, "shoc_wthv_sfc")) {
454  mf[lev], mf_comp, shoc_wthv_source, klo, -999);
455  mf_comp++;
456  } // shoc_wthv_sfc
457 
458  if (mf_comp != ncomp_mf) {
459  Abort(plotfile2d::format_2d_component_count_error(lev, mf_comp, ncomp_mf));
460  }
461  } // lev
462 
463  Vector<Geometry> my_geom = make_2d_plot_geometries(geom, finest_level);
464 
465  if (plotfile_type == PlotFileType::Amrex)
466  {
467  Print() << "Writing 2D native plotfile " << plotfilename << "\n";
468  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
469  GetVecOfConstPtrs(mf),
470  varnames, my_geom, t_new[0], istep, refRatio());
471  // Native AMReX 2D plotfiles write a JSON sidecar with catalog
472  // metadata for the selected output variables only.
473  plotfile2d::write_2d_metadata_json(plotfilename, varnames);
474  writeJobInfo(plotfilename);
475 
476 #ifdef ERF_USE_NETCDF
477  } else if (plotfile_type == PlotFileType::Netcdf) {
478  int lev = 0;
479  int l_which = 0;
480  const Real* p_lo = my_geom[lev].ProbLo();
481  const Real* p_hi = my_geom[lev].ProbHi();
482  const auto dx = my_geom[lev].CellSize();
483  writeNCPlotFile(lev, l_which, plotfilename, GetVecOfConstPtrs(mf), varnames, istep,
484  {p_lo[0],p_lo[1],p_lo[2]},{p_hi[0],p_hi[1],dx[2]}, {dx[0],dx[1],dx[2]},
485  my_geom[lev].Domain(), t_new[0], start_bdy_time, solverChoice, zlevels_stag[lev]);
486 #endif
487  } else {
488  // Here we assume the plotfile_type is PlotFileType::None
489  Print() << "Writing no 2D plotfile since plotfile_type is none" << std::endl;
490  }
491 }
void writeNCPlotFile(int lev, int which_subdomain, const std::string &dir, const Vector< const MultiFab * > &plotMF, const Vector< std::string > &plot_var_names, const Vector< int > &, Array< Real, AMREX_SPACEDIM > prob_lo, Array< Real, AMREX_SPACEDIM > prob_hi, Array< Real, AMREX_SPACEDIM > dx_in, const Box &subdomain, const Real time, const Real start_bdy_time, const SolverChoice &solverChoice, const Vector< Real > &zlevels_stag)
Definition: ERF_NCPlotFile.cpp:17
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Z_AtWFace(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:376
static amrex::Vector< std::string > PlotFileVarNames(amrex::Vector< std::string > plot_var_names)
Definition: ERF_Plotfile.cpp:253
void writeJobInfo(const std::string &dir) const
Definition: ERF_WriteJobInfo.cpp:9
Definition: ERF_ShocDriver.H:55
bool has_consumed_surface_flux_diagnostics() const
Definition: ERF_ShocDriver.H:89
const amrex::MultiFab & consumed_laten_flux_diagnostics() const
Definition: ERF_ShocDriver.H:101
bool owns_scalar_surface_fluxes() const
Definition: ERF_ShocDriver.cpp:889
bool has_native_diagnostics() const
Definition: ERF_ShocDriver.H:88
const amrex::MultiFab & shoc_olen_diagnostics() const
Definition: ERF_ShocDriver.H:106
const amrex::MultiFab & shoc_ustar_diagnostics() const
Definition: ERF_ShocDriver.H:105
const amrex::MultiFab & consumed_sens_flux_diagnostics() const
Definition: ERF_ShocDriver.H:97
const amrex::MultiFab & pblh_diagnostics() const
Definition: ERF_ShocDriver.H:95
const amrex::MultiFab & wthv_sec_diagnostics() const
Definition: ERF_ShocDriver.H:113
void write_2d_metadata_json(const std::string &plotfilename, const amrex::Vector< std::string > &varnames)
Definition: ERF_Plotfile2DMetadata.cpp:164
void fill_sensible_heat_flux_from_klevel_or_missing(MultiFab &dst, int dst_comp, const MultiFab *src, int src_k, Real missing_value)
Definition: ERF_Plotfile2DFill.cpp:59
std::string format_2d_component_count_error(int lev, int filled, int expected)
Definition: ERF_Plotfile2DUtils.cpp:86
void fill_component_from_klevel_or_value(MultiFab &dst, int dst_comp, const MultiFab *src, int src_k, Real missing_value, int src_comp)
Definition: ERF_Plotfile2DFill.cpp:44
AMREX_FORCE_INLINE bool use_native_shoc_consumed_flux_source(bool native_shoc_owns_scalar_fluxes, bool native_shoc_has_consumed_flux_diagnostics, bool host_flux_field_available) noexcept
Definition: ERF_Plotfile2DUtils.H:20
void fill_latent_heat_flux_from_klevel_or_missing(MultiFab &dst, int dst_comp, const MultiFab *src, int src_k, Real missing_value)
Definition: ERF_Plotfile2DFill.cpp:90
Here is the call graph for this function:

◆ Write3DPlotFile()

void ERF::Write3DPlotFile ( int  which,
PlotFileType  plotfile_type,
amrex::Vector< std::string >  plot_var_names 
)
266 {
267  auto dPlotTime0 = amrex::second();
268 
269  const Vector<std::string> varnames = PlotFileVarNames(plot_var_names);
270  const int ncomp_mf = static_cast<int>(varnames.size());
271 
272  int ncomp_cons = vars_new[0][Vars::cons].nComp();
273 
274  if (ncomp_mf == 0) return;
275 
276  // Lagrangian microphysics with AMR (TwoWay): bring fine-level moisture down
277  // to coarse cells so plotfiles at level 0 show the fine-deposit cloud.
278  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian
279  && solverChoice.coupling_type == CouplingType::TwoWay
280  && finest_level >= 1) {
281  micro->AverageDownMicroVars(finest_level);
282  for (int flev = finest_level-1; flev >= 0; --flev) {
284  }
285  }
286 
287  // We Fillpatch here because some of the derived quantities require derivatives
288  // which require ghost cells to be filled. We do not need to call FillPatcher
289  // because we don't need to set interior fine points.
290  // NOTE: the momenta here are only used as scratch space, the momenta themselves are not fillpatched
291 
292  // Level 0 FillPatch
294  &vars_new[0][Vars::yvel], &vars_new[0][Vars::zvel]});
295 
296  for (int lev = 1; lev <= finest_level; ++lev) {
297  bool fillset = false;
298  FillPatchFineLevel(lev, t_new[lev], {&vars_new[lev][Vars::cons], &vars_new[lev][Vars::xvel],
299  &vars_new[lev][Vars::yvel], &vars_new[lev][Vars::zvel]},
300  {&vars_new[lev][Vars::cons], &rU_new[lev], &rV_new[lev], &rW_new[lev]},
301  base_state[lev], base_state[lev], fillset);
302  }
303 
304  // Get qmoist pointers if using moisture
305  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
306  for (int lev = 0; lev <= finest_level; ++lev) {
307  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
308  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
309  }
310  }
311 
312  // Vector of MultiFabs for cell-centered data
313  Vector<MultiFab> mf(finest_level+1);
314  for (int lev = 0; lev <= finest_level; ++lev) {
315  mf[lev].define(grids[lev], dmap[lev], ncomp_mf, 0);
316  }
317 
318  // Vector of MultiFabs for nodal data
319  Vector<MultiFab> mf_nd(finest_level+1);
320  if ( SolverChoice::mesh_type != MeshType::ConstantDz) {
321  for (int lev = 0; lev <= finest_level; ++lev) {
322  BoxArray nodal_grids(grids[lev]); nodal_grids.surroundingNodes();
323  mf_nd[lev].define(nodal_grids, dmap[lev], 3, 0);
324  mf_nd[lev].setVal(0.);
325  }
326  }
327 
328  // Vector of MultiFabs for face-centered velocity
329  Vector<MultiFab> mf_u(finest_level+1);
330  Vector<MultiFab> mf_v(finest_level+1);
331  Vector<MultiFab> mf_w(finest_level+1);
332  if (m_plot_face_vels) {
333  for (int lev = 0; lev <= finest_level; ++lev) {
334  BoxArray grid_stag_u(grids[lev]); grid_stag_u.surroundingNodes(0);
335  BoxArray grid_stag_v(grids[lev]); grid_stag_v.surroundingNodes(1);
336  BoxArray grid_stag_w(grids[lev]); grid_stag_w.surroundingNodes(2);
337  mf_u[lev].define(grid_stag_u, dmap[lev], 1, 0);
338  mf_v[lev].define(grid_stag_v, dmap[lev], 1, 0);
339  mf_w[lev].define(grid_stag_w, dmap[lev], 1, 0);
340  MultiFab::Copy(mf_u[lev],vars_new[lev][Vars::xvel],0,0,1,0);
341  MultiFab::Copy(mf_v[lev],vars_new[lev][Vars::yvel],0,0,1,0);
342  MultiFab::Copy(mf_w[lev],vars_new[lev][Vars::zvel],0,0,1,0);
343  }
344  }
345 
346  // Array of MultiFabs for cell-centered velocity
347  Vector<MultiFab> mf_cc_vel(finest_level+1);
348 
349  if (containerHasElement(plot_var_names, "x_velocity" ) ||
350  containerHasElement(plot_var_names, "y_velocity" ) ||
351  containerHasElement(plot_var_names, "z_velocity" ) ||
352  containerHasElement(plot_var_names, "magvel" ) ||
353  containerHasElement(plot_var_names, "helicity" ) ||
354  containerHasElement(plot_var_names, "local_helicity") ||
355  containerHasElement(plot_var_names, "vorticity_x" ) ||
356  containerHasElement(plot_var_names, "vorticity_y" ) ||
357  containerHasElement(plot_var_names, "vorticity_z" ) ) {
358 
359  for (int lev = 0; lev <= finest_level; ++lev) {
360  mf_cc_vel[lev].define(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,1));
361  mf_cc_vel[lev].setVal(bogus_large_value);
362  average_face_to_cellcenter(mf_cc_vel[lev],0,
363  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],
364  &vars_new[lev][Vars::yvel],
365  &vars_new[lev][Vars::zvel]}, 1);
366  } // lev
367  } // if (vel or vort)
368 
369  // We need ghost cells if computing vorticity
370  if ( containerHasElement(plot_var_names, "vorticity_x")||
371  containerHasElement(plot_var_names, "vorticity_y") ||
372  containerHasElement(plot_var_names, "vorticity_z") )
373  {
374  amrex::Interpolater* mapper = &cell_cons_interp;
375  FillBdyCCVels(mf_cc_vel[0],geom[0]);
376  for (int lev = 1; lev <= finest_level; ++lev)
377  {
378  Vector<MultiFab*> fmf = {&(mf_cc_vel[lev]), &(mf_cc_vel[lev])};
379  Vector<Real> ftime = {t_new[lev], t_new[lev]};
380  Vector<MultiFab*> cmf = {&mf_cc_vel[lev-1], &mf_cc_vel[lev-1]};
381  Vector<Real> ctime = {t_new[lev], t_new[lev]};
382 
383  // Call FillPatch which ASSUMES that all ghost cells at lev-1 have already been filled
384  FillPatchTwoLevels(mf_cc_vel[lev], mf_cc_vel[lev].nGrowVect(), IntVect(0,0,0),
385  t_new[lev], cmf, ctime, fmf, ftime,
386  0, 0, mf_cc_vel[lev].nComp(), geom[lev-1], geom[lev],
387  refRatio(lev-1), mapper, domain_bcs_type,
389  FillBdyCCVels(mf_cc_vel[lev],geom[lev]);
390  } // lev
391  } // if (vort)
392 
393 
394  for (int lev = 0; lev <= finest_level; ++lev)
395  {
396  // Make sure getPgivenRTh and getTgivenRandRTh don't fail
397  if (check_for_nans) {
399  }
400 
401  int mf_comp = 0;
402 
403  BoxArray ba(vars_new[lev][Vars::cons].boxArray());
404  DistributionMapping dm = vars_new[lev][Vars::cons].DistributionMap();
405 
406  // First, copy any of the conserved state variables into the output plotfile
407  for (int i = 0; i < cons_names.size(); ++i) {
408  if (containerHasElement(plot_var_names, cons_names[i])) {
409  MultiFab::Copy(mf[lev],vars_new[lev][Vars::cons],i,mf_comp,1,0);
410  mf_comp++;
411  }
412  }
413 
414  // Next, check for velocities
415  if (containerHasElement(plot_var_names, "x_velocity")) {
416  MultiFab::Copy(mf[lev], mf_cc_vel[lev], 0, mf_comp, 1, 0);
417  mf_comp += 1;
418  }
419  if (containerHasElement(plot_var_names, "y_velocity")) {
420  MultiFab::Copy(mf[lev], mf_cc_vel[lev], 1, mf_comp, 1, 0);
421  mf_comp += 1;
422  }
423  if (containerHasElement(plot_var_names, "z_velocity")) {
424  MultiFab::Copy(mf[lev], mf_cc_vel[lev], 2, mf_comp, 1, 0);
425  mf_comp += 1;
426  }
427 
428  // Create multifabs for HSE and pressure fields used to derive other quantities
429  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp , 1);
430  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp , 1);
431  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
432  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp, 1);
433 
434  MultiFab pressure;
435 
436  if (solverChoice.anelastic[lev] == 0) {
437  if (containerHasElement(plot_var_names, "pressure") ||
438  containerHasElement(plot_var_names, "pert_pres") ||
439  containerHasElement(plot_var_names, "dpdx") ||
440  containerHasElement(plot_var_names, "dpdy") ||
441  containerHasElement(plot_var_names, "dpdz") ||
442  containerHasElement(plot_var_names, "eq_pot_temp") ||
443  containerHasElement(plot_var_names, "qsat"))
444  {
445  int ng = (containerHasElement(plot_var_names, "dpdx") || containerHasElement(plot_var_names, "dpdy") ||
446  containerHasElement(plot_var_names, "dpdz")) ? 1 : 0;
447 
448  // Allocate space for pressure
449  pressure.define(ba,dm,1,ng);
450 
451  if (ng > 0) {
452  // Default to p_hse as a way of filling ghost cells at domain boundaries
453  MultiFab::Copy(pressure,p_hse,0,0,1,1);
454  }
455 #ifdef _OPENMP
456 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
457 #endif
458  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
459  {
460  const Box& gbx = mfi.growntilebox(IntVect(ng,ng,0));
461 
462  const Array4<Real >& p_arr = pressure.array(mfi);
463  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
464  const int ncomp = vars_new[lev][Vars::cons].nComp();
465 
466  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
467  {
468  Real qv_for_p = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : 0;
469  const Real rhotheta = S_arr(i,j,k,RhoTheta_comp);
470  p_arr(i, j, k) = getPgivenRTh(rhotheta,qv_for_p);
471  });
472  } // mfi
473  pressure.FillBoundary(geom[lev].periodicity());
474  } // compute compressible pressure
475  } // not anelastic
476  else {
477  if (containerHasElement(plot_var_names, "dpdx") ||
478  containerHasElement(plot_var_names, "dpdy") ||
479  containerHasElement(plot_var_names, "dpdz") ||
480  containerHasElement(plot_var_names, "eq_pot_temp") ||
481  containerHasElement(plot_var_names, "qsat"))
482  {
483  // Copy p_hse into pressure if using anelastic
484  pressure.define(ba,dm,1,0);
485  MultiFab::Copy(pressure,p_hse,0,0,1,0);
486  }
487  }
488 
489  // ***************************************************************************************
490  // Finally, check for any derived quantities and compute them, inserting
491  // them into our output multifab
492  // ***************************************************************************************
493  auto calculate_derived = [&](const std::string& der_name,
494  MultiFab& src_mf,
495  decltype(derived::erf_dernull)& der_function)
496  {
497  if (containerHasElement(plot_var_names, der_name)) {
498  MultiFab dmf(mf[lev], make_alias, mf_comp, 1);
499 #ifdef _OPENMP
500 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
501 #endif
502  for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
503  {
504  const Box& bx = mfi.tilebox();
505  auto& dfab = dmf[mfi];
506  auto& sfab = src_mf[mfi];
507  auto& zfab = (*z_phys_cc[lev])[mfi];
508  der_function(bx, dfab, 0, 1, sfab, zfab, Geom(lev), t_new[0], nullptr, lev);
509  }
510 
511  mf_comp++;
512  }
513  }; // end calculate_derived
514  // ***************************************************************************************
515 
516  // *****************************************************************************************
517  // NOTE: All derived variables computed below **MUST MATCH THE ORDER** of "derived_names"
518  // defined in ERF.H
519  // *****************************************************************************************
520 
521  if (use_moisture) {
522  calculate_derived("temp", vars_new[lev][Vars::cons], derived::erf_dermoisttemp);
523  } else {
524  calculate_derived("temp", vars_new[lev][Vars::cons], derived::erf_dertemp);
525  }
526  calculate_derived("theta", vars_new[lev][Vars::cons], derived::erf_dertheta);
527  calculate_derived("KE", vars_new[lev][Vars::cons], derived::erf_derKE);
528  calculate_derived("scalar", vars_new[lev][Vars::cons], derived::erf_derscalar);
529  calculate_derived("soundspeed", vars_new[lev][Vars::cons], derived::erf_dersoundspeed);
530 
531  if (containerHasElement(plot_var_names, "reflectivity"))
532  {
533  if (solverChoice.moisture_type == MoistureType::Morrison ||
534  solverChoice.moisture_type == MoistureType::WSM6 ||
535  solverChoice.moisture_type == MoistureType::SAM) {
536  calculate_derived("reflectivity", vars_new[lev][Vars::cons], derived::erf_derreflectivity);
537  } else {
538  mf[lev].setVal(zero, mf_comp, 1, 0);
539  mf_comp++;
540  }
541  }
542 
543  if (containerHasElement(plot_var_names, "max_reflectivity"))
544  {
545  if (solverChoice.moisture_type == MoistureType::Morrison ||
546  solverChoice.moisture_type == MoistureType::WSM6 ||
547  solverChoice.moisture_type == MoistureType::SAM) {
548  calculate_derived("max_reflectivity", vars_new[lev][Vars::cons], derived::erf_dermaxreflectivity);
549  } else {
550  mf[lev].setVal(zero, mf_comp, 1, 0);
551  mf_comp++;
552  }
553  }
554 
555  if (solverChoice.moisture_type != MoistureType::None) {
556  calculate_derived("precipitable" , vars_new[lev][Vars::cons], derived::erf_derprecipitable);
557  }
558  calculate_derived("mucape" , vars_new[lev][Vars::cons], derived::erf_dermucape);
559 
560  calculate_derived("vorticity_x", mf_cc_vel[lev], derived::erf_dervortx);
561  calculate_derived("vorticity_y", mf_cc_vel[lev], derived::erf_dervorty);
562  calculate_derived("vorticity_z", mf_cc_vel[lev], derived::erf_dervortz);
563  calculate_derived("helicity", mf_cc_vel[lev], derived::erf_derhelicity);
564  calculate_derived("local_helicity", mf_cc_vel[lev], derived::erf_derlocalhelicity);
565  calculate_derived("magvel", mf_cc_vel[lev], derived::erf_dermagvel);
566 
567  if (containerHasElement(plot_var_names, "divU"))
568  {
569  MultiFab dmf(mf[lev], make_alias, mf_comp, 1);
570  Array<MultiFab const*, AMREX_SPACEDIM> u;
571  u[0] = &(vars_new[lev][Vars::xvel]);
572  u[1] = &(vars_new[lev][Vars::yvel]);
573  u[2] = &(vars_new[lev][Vars::zvel]);
574  compute_divergence (lev, dmf, u, geom[lev]);
575  mf_comp += 1;
576  }
577 
578  if (containerHasElement(plot_var_names, "pres_hse"))
579  {
580  MultiFab::Copy(mf[lev],p_hse,0,mf_comp,1,0);
581  mf_comp += 1;
582  }
583  if (containerHasElement(plot_var_names, "dens_hse"))
584  {
585  MultiFab::Copy(mf[lev],r_hse,0,mf_comp,1,0);
586  mf_comp += 1;
587  }
588  if (containerHasElement(plot_var_names, "theta_hse"))
589  {
590  MultiFab::Copy(mf[lev],th_hse,0,mf_comp,1,0);
591  mf_comp += 1;
592  }
593  if (containerHasElement(plot_var_names, "qv_hse"))
594  {
595  MultiFab::Copy(mf[lev],qv_hse,0,mf_comp,1,0);
596  mf_comp += 1;
597  }
598 
599  if (containerHasElement(plot_var_names, "pressure"))
600  {
601  if (solverChoice.anelastic[lev] == 1) {
602  MultiFab::Copy(mf[lev], p_hse, 0, mf_comp, 1, 0);
603  } else {
604  MultiFab::Copy(mf[lev], pressure, 0, mf_comp, 1, 0);
605  }
606 
607  mf_comp += 1;
608  }
609 
610  if (containerHasElement(plot_var_names, "pert_pres"))
611  {
612  if (solverChoice.anelastic[lev] == 1) {
613  MultiFab::Copy(mf[lev], pp_inc[lev], 0, mf_comp, 1, 0);
614  } else {
615  MultiFab::Copy(mf[lev], pressure, 0, mf_comp, 1, 0);
616  MultiFab::Subtract(mf[lev],p_hse,0,mf_comp,1,IntVect{0});
617  }
618  mf_comp += 1;
619  }
620 
621  if (containerHasElement(plot_var_names, "pert_dens"))
622  {
623 #ifdef _OPENMP
624 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
625 #endif
626  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
627  {
628  const Box& bx = mfi.tilebox();
629  const Array4<Real>& derdat = mf[lev].array(mfi);
630  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
631  const Array4<Real const>& r0_arr = r_hse.const_array(mfi);
632  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
633  derdat(i, j, k, mf_comp) = S_arr(i,j,k,Rho_comp) - r0_arr(i,j,k);
634  });
635  }
636  mf_comp ++;
637  }
638 
639  if (containerHasElement(plot_var_names, "buoyancy"))
640  {
641  MultiFab qt(mf[lev].boxArray(), mf[lev].DistributionMap(), 1, 1);
642  MultiFab b(mf[lev].boxArray(), mf[lev].DistributionMap(), 1, 0);
643  MultiFab S_prim(mf[lev].boxArray(), mf[lev].DistributionMap(),
644  vars_new[lev][Vars::cons].nComp()-1, 1);
645 
646  qt.setVal(0.);
647  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
648  if (solverChoice.moisture_type != MoistureType::None) {
649  make_qt(vars_new[lev][Vars::cons], qt, n_qstate_into_total);
650  }
651  cons_to_prim(vars_new[lev][Vars::cons], S_prim, 0);
652 
653  b.setVal(0.); // Need to initialize to zero because buoyancy not defined on faces at top and bottom of domain
654  make_buoyancy(lev, vars_new[lev], S_prim, qt, b, geom[lev], solverChoice, base_state[lev], n_qstate_into_total,
655  get_eb(lev), solverChoice.anelastic[lev]);
656  MultiFab::Copy(mf[lev], b, 0, mf_comp, 1, 0);
657  mf_comp ++;
658  }
659 
660  if (containerHasElement(plot_var_names, "eq_pot_temp"))
661  {
662 #ifdef _OPENMP
663 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
664 #endif
665  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
666  {
667  const Box& bx = mfi.tilebox();
668  const Array4<Real>& derdat = mf[lev].array(mfi);
669  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
670  const Array4<Real const>& p_arr = pressure.const_array(mfi);
671  const int ncomp = vars_new[lev][Vars::cons].nComp();
672  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
673  Real qv = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : zero;
674  Real qc = (use_moisture && (ncomp > RhoQ2_comp)) ? S_arr(i,j,k,RhoQ2_comp)/S_arr(i,j,k,Rho_comp) : zero;
675  Real T = getTgivenRandRTh(S_arr(i,j,k,Rho_comp), S_arr(i,j,k,RhoTheta_comp), qv);
676  Real fac = Cp_d + Cp_l*(qv + qc);
677  Real pv = erf_esatw(T)*Real(100.0);
678 
679  derdat(i, j, k, mf_comp) = T*std::pow((p_arr(i,j,k) - pv)/p_0, -R_d/fac)*std::exp(L_v*qv/(fac*T)) ;
680  });
681  }
682  mf_comp ++;
683  }
684 
685  if (containerHasElement(plot_var_names, "VPD"))
686  {
687 #ifdef _OPENMP
688 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
689 #endif
690  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
691  {
692  const Box& bx = mfi.tilebox();
693  const Array4<Real>& derdat = mf[lev].array(mfi);
694  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
695  const Array4<Real const>& p_arr = pressure.const_array(mfi);
696  const int ncomp = vars_new[lev][Vars::cons].nComp();
697  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
698  {
699  const Real qv = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : zero;
700 
701  const Real T = getTgivenRandRTh(S_arr(i,j,k,Rho_comp), S_arr(i,j,k,RhoTheta_comp), qv);
702  const Real e_sat = Real(100.0) * erf_esatw_cc(T);
703 
704  const Real P = p_arr(i,j,k);
705  const Real e_act = P * qv / (Real(0.622) + qv);
706 
707  derdat(i,j,k,mf_comp) = std::max(amrex::Real(0), e_sat - e_act) * Real(0.001);
708  });
709  }
710  mf_comp ++;
711  }
712 
713 #ifdef ERF_USE_WINDFARM
714  if ( containerHasElement(plot_var_names, "num_turb") and
715  (solverChoice.windfarm_type == WindFarmType::Fitch or solverChoice.windfarm_type == WindFarmType::EWP or
716  solverChoice.windfarm_type == WindFarmType::SimpleAD or solverChoice.windfarm_type == WindFarmType::GeneralAD) )
717  {
718  MultiFab::Copy(mf[lev],Nturb[lev],0,mf_comp,1,0);
719  mf_comp ++;
720  }
721 
722  if ( containerHasElement(plot_var_names, "SMark0") and
723  (solverChoice.windfarm_type == WindFarmType::Fitch or solverChoice.windfarm_type == WindFarmType::EWP or
724  solverChoice.windfarm_type == WindFarmType::SimpleAD or solverChoice.windfarm_type == WindFarmType::GeneralAD) )
725  {
726  MultiFab::Copy(mf[lev],SMark[lev],0,mf_comp,1,0);
727  mf_comp ++;
728  }
729 
730  if (containerHasElement(plot_var_names, "SMark1") and
731  (solverChoice.windfarm_type == WindFarmType::SimpleAD or solverChoice.windfarm_type == WindFarmType::GeneralAD))
732  {
733  MultiFab::Copy(mf[lev],SMark[lev],1,mf_comp,1,0);
734  mf_comp ++;
735  }
736 #endif
737 
738  // **********************************************************************************************
739  // Allocate space if we are computing any pressure gradients
740  // **********************************************************************************************
741 
742  Vector<MultiFab> gradp_temp; gradp_temp.resize(AMREX_SPACEDIM);
743  if (containerHasElement(plot_var_names, "dpdx") ||
744  containerHasElement(plot_var_names, "dpdy") ||
745  containerHasElement(plot_var_names, "dpdz") ||
746  containerHasElement(plot_var_names, "pres_hse_x") ||
747  containerHasElement(plot_var_names, "pres_hse_y"))
748  {
749  gradp_temp[GpVars::gpx].define(convert(ba, IntVect(1,0,0)), dm, 1, 1); gradp_temp[GpVars::gpx].setVal(0.);
750  gradp_temp[GpVars::gpy].define(convert(ba, IntVect(0,1,0)), dm, 1, 1); gradp_temp[GpVars::gpy].setVal(0.);
751  gradp_temp[GpVars::gpz].define(convert(ba, IntVect(0,0,1)), dm, 1, 1); gradp_temp[GpVars::gpz].setVal(0.);
752  }
753 
754  // **********************************************************************************************
755  // These are based on computing gradient of full pressure
756  // **********************************************************************************************
757 
758  if (solverChoice.anelastic[lev] == 0) {
759  if ( (containerHasElement(plot_var_names, "dpdx")) ||
760  (containerHasElement(plot_var_names, "dpdy")) ||
761  (containerHasElement(plot_var_names, "dpdz")) ) {
762  compute_gradp(pressure, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
763  get_eb(lev), gradp_temp, solverChoice);
764  }
765  }
766 
767  if (containerHasElement(plot_var_names, "dpdx"))
768  {
769  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
770  {
771  const Box& bx = mfi.tilebox();
772  const Array4<Real >& derdat = mf[lev].array(mfi);
773  const Array4<Real const>& gpx_arr = (solverChoice.anelastic[lev] == 1) ?
774  gradp[lev][GpVars::gpx].array(mfi) : gradp_temp[GpVars::gpx].array(mfi);
775  const Array4<Real const>& mf_mx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
776  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
777  derdat(i ,j ,k, mf_comp) = myhalf * (gpx_arr(i+1,j,k) + gpx_arr(i,j,k)) * mf_mx_arr(i,j,0);
778  });
779  }
780  mf_comp ++;
781  } // dpdx
782  if (containerHasElement(plot_var_names, "dpdy"))
783  {
784  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
785  {
786  const Box& bx = mfi.tilebox();
787  const Array4<Real >& derdat = mf[lev].array(mfi);
788  const Array4<Real const>& gpy_arr = (solverChoice.anelastic[lev] == 1) ?
789  gradp[lev][GpVars::gpy].array(mfi) : gradp_temp[GpVars::gpy].array(mfi);
790  const Array4<Real const>& mf_my_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
791  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
792  derdat(i ,j ,k, mf_comp) = myhalf * (gpy_arr(i,j+1,k) + gpy_arr(i,j,k)) * mf_my_arr(i,j,0);
793  });
794  }
795  mf_comp ++;
796  } // dpdy
797  if (containerHasElement(plot_var_names, "dpdz"))
798  {
799  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
800  {
801  const Box& bx = mfi.tilebox();
802  const Array4<Real >& derdat = mf[lev].array(mfi);
803  const Array4<Real const>& gpz_arr = (solverChoice.anelastic[lev] == 1) ?
804  gradp[lev][GpVars::gpz].array(mfi) : gradp_temp[GpVars::gpz].array(mfi);
805  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
806  derdat(i ,j ,k, mf_comp) = myhalf * (gpz_arr(i,j,k+1) + gpz_arr(i,j,k));
807  });
808  }
809  mf_comp ++;
810  } // dpdz
811 
812  // **********************************************************************************************
813  // These are based on computing gradient of basestate pressure
814  // **********************************************************************************************
815 
816  if ( (containerHasElement(plot_var_names, "pres_hse_x")) ||
817  (containerHasElement(plot_var_names, "pres_hse_y")) ) {
818  compute_gradp(p_hse, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
819  get_eb(lev), gradp_temp, solverChoice);
820  }
821 
822  if (containerHasElement(plot_var_names, "pres_hse_x"))
823  {
824  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
825  {
826  const Box& bx = mfi.tilebox();
827  const Array4<Real >& derdat = mf[lev].array(mfi);
828  const Array4<Real const>& gpx_arr = gradp_temp[0].array(mfi);
829  const Array4<Real const>& mf_mx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
830  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
831  derdat(i ,j ,k, mf_comp) = myhalf * (gpx_arr(i+1,j,k) + gpx_arr(i,j,k)) * mf_mx_arr(i,j,0);
832  });
833  }
834  mf_comp += 1;
835  } // pres_hse_x
836 
837  if (containerHasElement(plot_var_names, "pres_hse_y"))
838  {
839  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
840  {
841  const Box& bx = mfi.tilebox();
842  const Array4<Real >& derdat = mf[lev].array(mfi);
843  const Array4<Real const>& gpy_arr = gradp_temp[1].array(mfi);
844  const Array4<Real const>& mf_my_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
845  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
846  derdat(i ,j ,k, mf_comp) = myhalf * (gpy_arr(i,j+1,k) + gpy_arr(i,j,k)) * mf_my_arr(i,j,0);
847  });
848  }
849  mf_comp += 1;
850  } // pres_hse_y
851 
852  // **********************************************************************************************
853  // Metric terms
854  // **********************************************************************************************
855 
856  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
857  if (containerHasElement(plot_var_names, "z_phys"))
858  {
859  MultiFab::Copy(mf[lev],*z_phys_cc[lev],0,mf_comp,1,0);
860  mf_comp ++;
861  }
862 
863  if (containerHasElement(plot_var_names, "detJ"))
864  {
865  MultiFab::Copy(mf[lev],*detJ_cc[lev],0,mf_comp,1,0);
866  mf_comp ++;
867  }
868  } // use_terrain
869 
870  if (containerHasElement(plot_var_names, "mapfac")) {
871  amrex::Print() << "You are plotting a 3D version of mapfac; we suggest using the 2D plotfile instead" << std::endl;
872 #ifdef _OPENMP
873 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
874 #endif
875  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
876  {
877  const Box& bx = mfi.tilebox();
878  const Array4<Real>& derdat = mf[lev].array(mfi);
879  const Array4<Real>& mf_m = mapfac[lev][MapFacType::m_x]->array(mfi);
880  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
881  derdat(i ,j ,k, mf_comp) = mf_m(i,j,0);
882  });
883  }
884  mf_comp ++;
885  }
886 
887  if (containerHasElement(plot_var_names, "lat_m")) {
888  amrex::Print() << "You are plotting a 3D version of lat_m; we suggest using the 2D plotfile instead" << std::endl;
889  if (lat_m[lev]) {
890 #ifdef _OPENMP
891 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
892 #endif
893  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
894  {
895  const Box& bx = mfi.tilebox();
896  const Array4<Real>& derdat = mf[lev].array(mfi);
897  const Array4<Real>& data = lat_m[lev]->array(mfi);
898  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
899  derdat(i, j, k, mf_comp) = data(i,j,0);
900  });
901  }
902  } else {
903  mf[lev].setVal(0.0,mf_comp,1,0);
904  }
905  mf_comp++;
906  } // lat_m
907 
908  if (containerHasElement(plot_var_names, "lon_m")) {
909  amrex::Print() << "You are plotting a 3D version of lon_m; we suggest using the 2D plotfile instead" << std::endl;
910  if (lon_m[lev]) {
911 #ifdef _OPENMP
912 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
913 #endif
914  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
915  {
916  const Box& bx = mfi.tilebox();
917  const Array4<Real>& derdat = mf[lev].array(mfi);
918  const Array4<Real>& data = lon_m[lev]->array(mfi);
919  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
920  derdat(i, j, k, mf_comp) = data(i,j,0);
921  });
922  }
923  } else {
924  mf[lev].setVal(0.0,mf_comp,1,0);
925  }
926  mf_comp++;
927  } // lon_m
928 
930  if (containerHasElement(plot_var_names, "u_t_avg")) {
931 #ifdef _OPENMP
932 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
933 #endif
934  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
935  {
936  const Box& bx = mfi.tilebox();
937  const Array4<Real>& derdat = mf[lev].array(mfi);
938  const Array4<Real>& data = vel_t_avg[lev]->array(mfi);
939  const Real norm = t_avg_cnt[lev];
940  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
941  {
942  derdat(i ,j ,k, mf_comp) = data(i,j,k,0) / norm;
943  });
944  }
945  mf_comp ++;
946  }
947 
948  if (containerHasElement(plot_var_names, "v_t_avg")) {
949 #ifdef _OPENMP
950 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
951 #endif
952  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
953  {
954  const Box& bx = mfi.tilebox();
955  const Array4<Real>& derdat = mf[lev].array(mfi);
956  const Array4<Real>& data = vel_t_avg[lev]->array(mfi);
957  const Real norm = t_avg_cnt[lev];
958  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
959  {
960  derdat(i ,j ,k, mf_comp) = data(i,j,k,1) / norm;
961  });
962  }
963  mf_comp ++;
964  }
965 
966  if (containerHasElement(plot_var_names, "w_t_avg")) {
967 #ifdef _OPENMP
968 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
969 #endif
970  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
971  {
972  const Box& bx = mfi.tilebox();
973  const Array4<Real>& derdat = mf[lev].array(mfi);
974  const Array4<Real>& data = vel_t_avg[lev]->array(mfi);
975  const Real norm = t_avg_cnt[lev];
976  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
977  {
978  derdat(i ,j ,k, mf_comp) = data(i,j,k,2) / norm;
979  });
980  }
981  mf_comp ++;
982  }
983 
984  if (containerHasElement(plot_var_names, "umag_t_avg")) {
985 #ifdef _OPENMP
986 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
987 #endif
988  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
989  {
990  const Box& bx = mfi.tilebox();
991  const Array4<Real>& derdat = mf[lev].array(mfi);
992  const Array4<Real>& data = vel_t_avg[lev]->array(mfi);
993  const Real norm = t_avg_cnt[lev];
994  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
995  {
996  derdat(i ,j ,k, mf_comp) = data(i,j,k,3) / norm;
997  });
998  }
999  mf_comp ++;
1000  }
1001  }
1002 
1003  const MultiFab* eta_src = nullptr;
1004  const bool have_native_shoc_diagnostics =
1005 #ifdef ERF_USE_NATIVE_SHOC
1006  solverChoice.turbChoice[lev].uses_native_shoc() &&
1007  native_shoc_driver[lev] &&
1008  native_shoc_driver[lev]->has_native_diagnostics();
1009 #else
1010  false;
1011 #endif
1012  if (solverChoice.turbChoice[lev].use_kturb) {
1013 #ifdef ERF_USE_NATIVE_SHOC
1014  if (have_native_shoc_diagnostics) {
1015  eta_src = &native_shoc_driver[lev]->native_diagnostics();
1016  } else
1017 #endif
1018  {
1019  eta_src = eddyDiffs_lev[lev].get();
1020  }
1021  }
1022 
1023  if (containerHasElement(plot_var_names, "nut")) {
1024  MultiFab dmf(mf[lev], make_alias, mf_comp, 1);
1025  MultiFab cmf(vars_new[lev][Vars::cons], make_alias, 0, 1); // to provide rho only
1026 #ifdef _OPENMP
1027 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1028 #endif
1029  for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1030  {
1031  const Box& bx = mfi.tilebox();
1032  auto prim = dmf[mfi].array();
1033  auto const cons = cmf[mfi].const_array();
1034  auto const diff = (eta_src) ? eta_src->const_array(mfi) :
1035  Array4<const Real>{};
1036  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1037  {
1038  const Real rho = cons(i, j, k, Rho_comp);
1039  const Real Kmv = diff(i, j, k, EddyDiff::Mom_v);
1040  prim(i,j,k) = Kmv / rho;
1041  });
1042  }
1043 
1044  mf_comp++;
1045  }
1046 
1047  const MultiFab* shoc_or_host_eddy = nullptr;
1048 #ifdef ERF_USE_NATIVE_SHOC
1049  if (have_native_shoc_diagnostics) {
1050  shoc_or_host_eddy = &native_shoc_driver[lev]->native_diagnostics();
1051  } else
1052 #endif
1053  {
1054  shoc_or_host_eddy = eddyDiffs_lev[lev].get();
1055  }
1056 
1057  if (containerHasElement(plot_var_names, "Kmv")) {
1058  MultiFab::Copy(mf[lev],*shoc_or_host_eddy,EddyDiff::Mom_v,mf_comp,1,0);
1059  mf_comp ++;
1060  }
1061  if (containerHasElement(plot_var_names, "Kmh")) {
1062  MultiFab::Copy(mf[lev],*eddyDiffs_lev[lev],EddyDiff::Mom_h,mf_comp,1,0);
1063  mf_comp ++;
1064  }
1065  if (containerHasElement(plot_var_names, "Khv")) {
1066  MultiFab::Copy(mf[lev],*shoc_or_host_eddy,EddyDiff::Theta_v,mf_comp,1,0);
1067  mf_comp ++;
1068  }
1069  if (containerHasElement(plot_var_names, "Khh")) {
1070  MultiFab::Copy(mf[lev],*eddyDiffs_lev[lev],EddyDiff::Theta_h,mf_comp,1,0);
1071  mf_comp ++;
1072  }
1073  if (containerHasElement(plot_var_names, "Lturb")) {
1074  MultiFab::Copy(mf[lev],*shoc_or_host_eddy,EddyDiff::Turb_lengthscale,mf_comp,1,0);
1075  mf_comp ++;
1076  }
1077  auto copy_native_shoc_diagnostic = [&](const MultiFab* src) {
1078  if (src != nullptr) {
1079  MultiFab::Copy(mf[lev], *src, 0, mf_comp, 1, 0);
1080  } else {
1081  mf[lev].setVal(-999, mf_comp, 1, 0);
1082  }
1083  mf_comp ++;
1084  };
1085  // Native SHOC pblh is diagnosed in meters AGL and is copied through
1086  // unchanged into the plotfile diagnostic field.
1087  if (containerHasElement(plot_var_names, "pblh")) {
1088  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1089  ? &native_shoc_driver[lev]->pblh_diagnostics()
1090  : nullptr);
1091  }
1092  if (containerHasElement(plot_var_names, "shoc_cldfrac")) {
1093  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1094  ? &native_shoc_driver[lev]->shoc_cldfrac_diagnostics()
1095  : nullptr);
1096  }
1097  if (containerHasElement(plot_var_names, "shoc_ql")) {
1098  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1099  ? &native_shoc_driver[lev]->shoc_ql_diagnostics()
1100  : nullptr);
1101  }
1102  if (containerHasElement(plot_var_names, "shoc_ql2")) {
1103  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1104  ? &native_shoc_driver[lev]->shoc_ql2_diagnostics()
1105  : nullptr);
1106  }
1107  if (containerHasElement(plot_var_names, "shoc_cond")) {
1108  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1109  ? &native_shoc_driver[lev]->shoc_cond_diagnostics()
1110  : nullptr);
1111  }
1112  if (containerHasElement(plot_var_names, "wqls_sec")) {
1113  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1114  ? &native_shoc_driver[lev]->wqls_sec_diagnostics()
1115  : nullptr);
1116  }
1117  if (containerHasElement(plot_var_names, "wthv_sec")) {
1118  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1119  ? &native_shoc_driver[lev]->wthv_sec_diagnostics()
1120  : nullptr);
1121  }
1122  if (containerHasElement(plot_var_names, "w_sec")) {
1123  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1124  ? &native_shoc_driver[lev]->w_sec_diagnostics()
1125  : nullptr);
1126  }
1127  if (containerHasElement(plot_var_names, "thl_sec")) {
1128  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1129  ? &native_shoc_driver[lev]->thl_sec_diagnostics()
1130  : nullptr);
1131  }
1132  if (containerHasElement(plot_var_names, "qw_sec")) {
1133  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1134  ? &native_shoc_driver[lev]->qw_sec_diagnostics()
1135  : nullptr);
1136  }
1137  if (containerHasElement(plot_var_names, "qwthl_sec")) {
1138  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1139  ? &native_shoc_driver[lev]->qwthl_sec_diagnostics()
1140  : nullptr);
1141  }
1142  if (containerHasElement(plot_var_names, "wthl_sec")) {
1143  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1144  ? &native_shoc_driver[lev]->wthl_sec_diagnostics()
1145  : nullptr);
1146  }
1147  if (containerHasElement(plot_var_names, "wqw_sec")) {
1148  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1149  ? &native_shoc_driver[lev]->wqw_sec_diagnostics()
1150  : nullptr);
1151  }
1152  if (containerHasElement(plot_var_names, "w3")) {
1153  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1154  ? &native_shoc_driver[lev]->w3_diagnostics()
1155  : nullptr);
1156  }
1157  if (containerHasElement(plot_var_names, "brunt")) {
1158  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1159  ? &native_shoc_driver[lev]->brunt_diagnostics()
1160  : nullptr);
1161  }
1162  if (containerHasElement(plot_var_names, "isotropy")) {
1163  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1164  ? &native_shoc_driver[lev]->isotropy_diagnostics()
1165  : nullptr);
1166  }
1167  if (containerHasElement(plot_var_names, "shear_prod")) {
1168  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1169  ? &native_shoc_driver[lev]->shear_prod_diagnostics()
1170  : nullptr);
1171  }
1172  if (containerHasElement(plot_var_names, "buoy_prod")) {
1173  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1174  ? &native_shoc_driver[lev]->buoy_prod_diagnostics()
1175  : nullptr);
1176  }
1177  if (containerHasElement(plot_var_names, "diss_tke")) {
1178  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1179  ? &native_shoc_driver[lev]->diss_tke_diagnostics()
1180  : nullptr);
1181  }
1182  if (containerHasElement(plot_var_names, "walldist")) {
1183  MultiFab::Copy(mf[lev],*walldist[lev],0,mf_comp,1,0);
1184  mf_comp ++;
1185  }
1186  if (containerHasElement(plot_var_names, "diss")) {
1187  MultiFab::Copy(mf[lev],*SFS_diss_lev[lev],0,mf_comp,1,0);
1188  mf_comp ++;
1189  }
1190 
1191  // TODO: The size of the q variables can vary with different
1192  // moisture models. Therefore, certain components may
1193  // reside at different indices. For example, Kessler is
1194  // warm but precipitating. This puts qp at index three
1195  // However, SAM is cold and precipitating so qp is index Real(4.)
1196  // Need to built an external enum struct or a better pathway.
1197 
1198  // NOTE: Protect against accessing non-existent data
1199  if (use_moisture) {
1200  int n_qstate_moist = micro->Get_Qstate_Moist_Size();
1201  int n_qstate_moist_numconc = micro->Get_Qstate_Moist_NumConc_Size();
1202 
1203  // Moist density
1204  if(containerHasElement(plot_var_names, "moist_density"))
1205  {
1206  int n_start = RhoQ1_comp; // qv
1207  int n_end = RhoQ2_comp; // qc
1208  if (n_qstate_moist > 3) n_end = RhoQ3_comp; // qi
1209  MultiFab::Copy(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1210  for (int n_comp(n_start); n_comp <= n_end; ++n_comp) {
1211  MultiFab::Add(mf[lev], vars_new[lev][Vars::cons], n_comp, mf_comp, 1, 0);
1212  }
1213  mf_comp += 1;
1214  }
1215 
1216  if(containerHasElement(plot_var_names, "qv") && (n_qstate_moist >= 1))
1217  {
1218  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ1_comp, mf_comp, 1, 0);
1219  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp , mf_comp, 1, 0);
1220  mf_comp += 1;
1221  }
1222 
1223  if(containerHasElement(plot_var_names, "qc") && (n_qstate_moist >= 2))
1224  {
1225  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ2_comp, mf_comp, 1, 0);
1226  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp , mf_comp, 1, 0);
1227  mf_comp += 1;
1228  }
1229 
1230  if(containerHasElement(plot_var_names, "qi") && (n_qstate_moist >= 4))
1231  {
1232  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ3_comp, mf_comp, 1, 0);
1233  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp , mf_comp, 1, 0);
1234  mf_comp += 1;
1235  }
1236 
1237  if(containerHasElement(plot_var_names, "qrain") && (n_qstate_moist >= 3))
1238  {
1239  int n_start = (n_qstate_moist > 3) ? RhoQ4_comp : RhoQ3_comp;
1240  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], n_start , mf_comp, 1, 0);
1241  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1242  mf_comp += 1;
1243  }
1244 
1245  if(containerHasElement(plot_var_names, "qsnow") && (n_qstate_moist >= 5))
1246  {
1247  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ5_comp, mf_comp, 1, 0);
1248  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1249  mf_comp += 1;
1250  }
1251 
1252  if(containerHasElement(plot_var_names, "qgraup") && (n_qstate_moist >= 6))
1253  {
1254  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ6_comp, mf_comp, 1, 0);
1255  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1256  mf_comp += 1;
1257  }
1258 
1259  if(containerHasElement(plot_var_names, "nc") && (n_qstate_moist >= 7))
1260  {
1261  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ7_comp, mf_comp, 1, 0);
1262  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1263  mf_comp += 1;
1264  }
1265 
1266  if(containerHasElement(plot_var_names, "ni") && (n_qstate_moist >= 8))
1267  {
1268  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ8_comp, mf_comp, 1, 0);
1269  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1270  mf_comp += 1;
1271  }
1272 
1273  if(containerHasElement(plot_var_names, "nr") && (n_qstate_moist >= 9))
1274  {
1275  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ9_comp, mf_comp, 1, 0);
1276  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1277  mf_comp += 1;
1278  }
1279 
1280  if(containerHasElement(plot_var_names, "ns") && (n_qstate_moist >= 10))
1281  {
1282  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ10_comp, mf_comp, 1, 0);
1283  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1284  mf_comp += 1;
1285  }
1286 
1287  if(containerHasElement(plot_var_names, "ng") && (n_qstate_moist >= 11))
1288  {
1289  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], RhoQ11_comp, mf_comp, 1, 0);
1290  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1291  mf_comp += 1;
1292  }
1293 
1294  // Precipitating + non-precipitating components
1295  //--------------------------------------------------------------------------
1296  if(containerHasElement(plot_var_names, "qt"))
1297  {
1298  int n_start = RhoQ1_comp; // qv
1299  int n_end = n_start + n_qstate_moist - n_qstate_moist_numconc;
1300  MultiFab::Copy(mf[lev], vars_new[lev][Vars::cons], n_start, mf_comp, 1, 0);
1301 
1302  for (int n_comp(n_start+1); n_comp < n_end; ++n_comp) {
1303  MultiFab::Add(mf[lev], vars_new[lev][Vars::cons], n_comp, mf_comp, 1, 0);
1304  }
1305 
1306  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp , mf_comp, 1, 0);
1307 
1308  mf_comp += 1;
1309  }
1310 
1311  // Non-precipitating components
1312  //--------------------------------------------------------------------------
1313  if (containerHasElement(plot_var_names, "qn"))
1314  {
1315  int n_start = RhoQ1_comp; // qv
1316  int n_end = RhoQ2_comp; // qc
1317  if (n_qstate_moist > 3) n_end = RhoQ3_comp; // qi
1318  MultiFab::Copy(mf[lev], vars_new[lev][Vars::cons], n_start, mf_comp, 1, 0);
1319  for (int n_comp(n_start+1); n_comp <= n_end; ++n_comp) {
1320  MultiFab::Add(mf[lev], vars_new[lev][Vars::cons], n_comp, mf_comp, 1, 0);
1321  }
1322  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp , mf_comp, 1, 0);
1323  mf_comp += 1;
1324  }
1325 
1326  // Precipitating components
1327  //--------------------------------------------------------------------------
1328  if(containerHasElement(plot_var_names, "qp") && (n_qstate_moist >= 3))
1329  {
1330  int n_start = (n_qstate_moist > 3) ? RhoQ4_comp : RhoQ3_comp;
1331  int n_end = ncomp_cons - 1 - n_qstate_moist_numconc;
1332  MultiFab::Copy( mf[lev], vars_new[lev][Vars::cons], n_start, mf_comp, 1, 0);
1333  for (int n_comp(n_start+1); n_comp <= n_end; ++n_comp) {
1334  MultiFab::Add( mf[lev], vars_new[lev][Vars::cons], n_comp, mf_comp, 1, 0);
1335  }
1336  MultiFab::Divide(mf[lev], vars_new[lev][Vars::cons], Rho_comp , mf_comp, 1, 0);
1337  mf_comp += 1;
1338  }
1339 
1340  if (containerHasElement(plot_var_names, "qsat"))
1341  {
1342 #ifdef _OPENMP
1343 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1344 #endif
1345  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
1346  {
1347  const Box& bx = mfi.tilebox();
1348  const Array4<Real>& derdat = mf[lev].array(mfi);
1349  const Array4<Real const>& p_arr = pressure.array(mfi);
1350  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
1351  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1352  {
1353  Real qv = S_arr(i,j,k,RhoQ1_comp) / S_arr(i,j,k,Rho_comp);
1354  Real T = getTgivenRandRTh(S_arr(i,j,k,Rho_comp), S_arr(i,j,k,RhoTheta_comp), qv);
1355  Real p = p_arr(i,j,k) * Real(0.01);
1356  erf_qsatw(T, p, derdat(i,j,k,mf_comp));
1357  });
1358  }
1359  mf_comp ++;
1360  }
1361 
1362  if ( (solverChoice.moisture_type == MoistureType::Kessler) ||
1363  (solverChoice.moisture_type == MoistureType::Morrison_NoIce) ||
1364  (solverChoice.moisture_type == MoistureType::SAM_NoIce) )
1365  {
1366  if (containerHasElement(plot_var_names, "rain_accum"))
1367  {
1368  MultiFab::Copy(mf[lev],*(qmoist[lev][0]),0,mf_comp,1,0);
1369  mf_comp += 1;
1370  }
1371  if (containerHasElement(plot_var_names, "rel_humidity")) {
1372  Print() << "Warning: plot variable \"rel_humidity\" is not available with Kessler moisture model.\n";
1373  mf[lev].setVal(0.0, mf_comp, 1, 0);
1374  mf_comp += 1;
1375  }
1376  }
1377  else if ( (solverChoice.moisture_type == MoistureType::SAM) ||
1378  (solverChoice.moisture_type == MoistureType::Morrison) ||
1379  (solverChoice.moisture_type == MoistureType::WSM6) )
1380  {
1381  if (containerHasElement(plot_var_names, "rain_accum"))
1382  {
1383  MultiFab::Copy(mf[lev],*(qmoist[lev][0]),0,mf_comp,1,0);
1384  mf_comp += 1;
1385  }
1386  if (containerHasElement(plot_var_names, "snow_accum"))
1387  {
1388  MultiFab::Copy(mf[lev],*(qmoist[lev][1]),0,mf_comp,1,0);
1389  mf_comp += 1;
1390  }
1391  if (containerHasElement(plot_var_names, "graup_accum"))
1392  {
1393  MultiFab::Copy(mf[lev],*(qmoist[lev][2]),0,mf_comp,1,0);
1394  mf_comp += 1;
1395  }
1396  if (containerHasElement(plot_var_names, "rel_humidity")) {
1397  Print() << "Warning: plot variable \"rel_humidity\" is not available with SAM moisture model.\n";
1398  mf[lev].setVal(0.0, mf_comp, 1, 0);
1399  mf_comp += 1;
1400  }
1401  }
1402  else if(solverChoice.moisture_type == MoistureType::SuperDroplets)
1403  {
1404  if (containerHasElement(plot_var_names, "rain_accum")) {
1405  MultiFab::Copy(mf[lev],*(qmoist[lev][6]),0,mf_comp,1,0);
1406  mf_comp += 1;
1407  }
1408  if (containerHasElement(plot_var_names, "rel_humidity")) {
1409  MultiFab::Copy(mf[lev],*(qmoist[lev][5]),0,mf_comp,1,0);
1410  mf_comp += 1;
1411  }
1412  if (containerHasElement(plot_var_names, "condensation_rate")) {
1413  MultiFab::Copy(mf[lev],*(qmoist[lev][3]),0,mf_comp,1,0);
1414  mf_comp += 1;
1415  }
1416  }
1417 
1418  } // if use_moisture
1419 
1420  if (containerHasElement(plot_var_names, "terrain_IB_mask"))
1421  {
1422  MultiFab* terrain_blank = terrain_blanking[lev].get();
1423  MultiFab::Copy(mf[lev],*terrain_blank,0,mf_comp,1,0);
1424  mf_comp ++;
1425  }
1426 
1427  if (containerHasElement(plot_var_names, "volfrac")) {
1428  if ( solverChoice.terrain_type == TerrainType::EB ||
1429  solverChoice.terrain_type == TerrainType::ImmersedForcing)
1430  {
1431  MultiFab::Copy(mf[lev], EBFactory(lev).getVolFrac(), 0, mf_comp, 1, 0);
1432  } else {
1433  mf[lev].setVal(1.0, mf_comp, 1, 0);
1434  }
1435  mf_comp += 1;
1436  }
1437 
1438 #ifdef ERF_COMPUTE_ERROR
1439  // Next, check for error in velocities and if desired, output them -- note we output none or all, not just some
1440  if (containerHasElement(plot_var_names, "xvel_err") ||
1441  containerHasElement(plot_var_names, "yvel_err") ||
1442  containerHasElement(plot_var_names, "zvel_err"))
1443  {
1444  //
1445  // Moving terrain ANALYTICAL
1446  //
1447  Real H = geom[lev].ProbHi()[2];
1448  Real Ampl = Real(0.16);
1449  Real wavelength = Real(100.);
1450  Real kp = two * PI / wavelength;
1451  Real g = CONST_GRAV;
1452  Real omega = std::sqrt(g * kp);
1453  Real omega_t = omega * t_new[lev];
1454 
1455  const auto dx = geom[lev].CellSizeArray();
1456 
1457 #ifdef _OPENMP
1458 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1459 #endif
1460  for (MFIter mfi(mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
1461  {
1462  const Box& bx = mfi.validbox();
1463  Box xbx(bx); xbx.surroundingNodes(0);
1464  const Array4<Real> xvel_arr = vars_new[lev][Vars::xvel].array(mfi);
1465  const Array4<Real> zvel_arr = vars_new[lev][Vars::zvel].array(mfi);
1466 
1467  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1468 
1469  ParallelFor(xbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1470  {
1471  Real x = i * dx[0];
1472  Real z = fourth * (z_nd(i,j,k) + z_nd(i,j+1,k) + z_nd(i,j,k+1) + z_nd(i,j+1,k+1));
1473 
1474  Real z_base = Ampl * std::sin(kp * x - omega_t);
1475  z -= z_base;
1476 
1477  Real fac = std::cosh( kp * (z - H) ) / std::sinh(kp * H);
1478 
1479  xvel_arr(i,j,k) -= -Ampl * omega * fac * std::sin(kp * x - omega_t);
1480  });
1481 
1482  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1483  {
1484  Real x = (i + myhalf) * dx[0];
1485  Real z = fourth * ( z_nd(i,j,k) + z_nd(i+1,j,k) + z_nd(i,j+1,k) + z_nd(i+1,j+1,k));
1486 
1487  Real z_base = Ampl * std::sin(kp * x - omega_t);
1488  z -= z_base;
1489 
1490  Real fac = std::sinh( kp * (z - H) ) / std::sinh(kp * H);
1491 
1492  zvel_arr(i,j,k) -= Ampl * omega * fac * std::cos(kp * x - omega_t);
1493  });
1494  }
1495 
1496  MultiFab temp_mf(mf[lev].boxArray(), mf[lev].DistributionMap(), AMREX_SPACEDIM, 0);
1497  average_face_to_cellcenter(temp_mf,0,
1498  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],&vars_new[lev][Vars::yvel],&vars_new[lev][Vars::zvel]});
1499 
1500  if (containerHasElement(plot_var_names, "xvel_err")) {
1501  MultiFab::Copy(mf[lev],temp_mf,0,mf_comp,1,0);
1502  mf_comp += 1;
1503  }
1504  if (containerHasElement(plot_var_names, "yvel_err")) {
1505  MultiFab::Copy(mf[lev],temp_mf,1,mf_comp,1,0);
1506  mf_comp += 1;
1507  }
1508  if (containerHasElement(plot_var_names, "zvel_err")) {
1509  MultiFab::Copy(mf[lev],temp_mf,2,mf_comp,1,0);
1510  mf_comp += 1;
1511  }
1512 
1513  // Now restore the velocities to what they were
1514 #ifdef _OPENMP
1515 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1516 #endif
1517  for (MFIter mfi(mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
1518  {
1519  const Box& bx = mfi.validbox();
1520  Box xbx(bx); xbx.surroundingNodes(0);
1521 
1522  const Array4<Real> xvel_arr = vars_new[lev][Vars::xvel].array(mfi);
1523  const Array4<Real> zvel_arr = vars_new[lev][Vars::zvel].array(mfi);
1524 
1525  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1526 
1527  ParallelFor(xbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1528  {
1529  Real x = i * dx[0];
1530  Real z = fourth * (z_nd(i,j,k) + z_nd(i,j+1,k) + z_nd(i,j,k+1) + z_nd(i,j+1,k+1));
1531  Real z_base = Ampl * std::sin(kp * x - omega_t);
1532 
1533  z -= z_base;
1534 
1535  Real fac = std::cosh( kp * (z - H) ) / std::sinh(kp * H);
1536  xvel_arr(i,j,k) += -Ampl * omega * fac * std::sin(kp * x - omega_t);
1537  });
1538  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1539  {
1540  Real x = (i + myhalf) * dx[0];
1541  Real z = fourth * ( z_nd(i,j,k) + z_nd(i+1,j,k) + z_nd(i,j+1,k) + z_nd(i+1,j+1,k));
1542  Real z_base = Ampl * std::sin(kp * x - omega_t);
1543 
1544  z -= z_base;
1545  Real fac = std::sinh( kp * (z - H) ) / std::sinh(kp * H);
1546 
1547  zvel_arr(i,j,k) += Ampl * omega * fac * std::cos(kp * x - omega_t);
1548  });
1549  }
1550  } // end xvel_err, yvel_err, zvel_err
1551 
1552  if (containerHasElement(plot_var_names, "pp_err"))
1553  {
1554  // Moving terrain ANALYTICAL
1555 #ifdef _OPENMP
1556 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1557 #endif
1558  for ( MFIter mfi(mf[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
1559  {
1560  const Box& bx = mfi.tilebox();
1561  const Array4<Real>& derdat = mf[lev].array(mfi);
1562  const Array4<Real const>& p0_arr = p_hse.const_array(mfi);
1563  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
1564 
1565  const auto dx = geom[lev].CellSizeArray();
1566  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1567  const Array4<Real const>& p_arr = pressure.const_array(mfi);
1568  const Array4<Real const>& r0_arr = r_hse.const_array(mfi);
1569 
1570  Real H = geom[lev].ProbHi()[2];
1571  Real Ampl = Real(0.16);
1572  Real wavelength = Real(100.);
1573  Real kp = two * PI / wavelength;
1574  Real g = CONST_GRAV;
1575  Real omega = std::sqrt(g * kp);
1576  Real omega_t = omega * t_new[lev];
1577 
1578  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1579  {
1580  derdat(i, j, k, mf_comp) = p_arr(i,j,k) - p0_arr(i,j,k);
1581 
1582  Real rho_hse = r0_arr(i,j,k);
1583 
1584  Real x = (i + myhalf) * dx[0];
1585  Real z = Real(0.125) * ( z_nd(i,j,k ) + z_nd(i+1,j,k ) + z_nd(i,j+1,k ) + z_nd(i+1,j+1,k )
1586  +z_nd(i,j,k+1) + z_nd(i+1,j,k+1) + z_nd(i,j+1,k+1) + z_nd(i+1,j+1,k+1) );
1587  Real z_base = Ampl * std::sin(kp * x - omega_t);
1588 
1589  z -= z_base;
1590  Real fac = std::cosh( kp * (z - H) ) / std::sinh(kp * H);
1591  Real pprime_exact = -(Ampl * omega * omega / kp) * fac *
1592  std::sin(kp * x - omega_t) * r0_arr(i,j,k);
1593 
1594  derdat(i,j,k,mf_comp) -= pprime_exact;
1595  });
1596  }
1597  mf_comp += 1;
1598  }
1599 #endif
1600 
1601  if (solverChoice.rad_type != RadiationType::None) {
1602  if (containerHasElement(plot_var_names, "qsrc_sw")) {
1603  MultiFab::Copy(mf[lev], *(qheating_rates[lev]), 0, mf_comp, 1, 0);
1604  mf_comp += 1;
1605  }
1606  if (containerHasElement(plot_var_names, "qsrc_lw")) {
1607  MultiFab::Copy(mf[lev], *(qheating_rates[lev]), 1, mf_comp, 1, 0);
1608  mf_comp += 1;
1609  }
1610  }
1611 
1612  // *****************************************************************************************
1613  // End of derived variables corresponding to "derived_names" in ERF.H
1614  //
1615  // Particles and microphysics can provide additional outputs, which are handled below.
1616  // *****************************************************************************************
1617 
1618 #ifdef ERF_USE_PARTICLES
1619  const auto& particles_namelist( particleData.getNames() );
1620 
1621  if (containerHasElement(plot_var_names, "tracer_particles_count")) {
1622  if (particles_namelist.size() == 0) {
1623  MultiFab temp_dat(mf[lev].boxArray(), mf[lev].DistributionMap(), 1, 0);
1624  temp_dat.setVal(0);
1625  MultiFab::Copy(mf[lev], temp_dat, 0, mf_comp, 1, 0);
1626  mf_comp += 1;
1627  } else {
1628  for (ParticlesNamesVector::size_type i = 0; i < particles_namelist.size(); i++) {
1629  if (containerHasElement(plot_var_names, std::string(particles_namelist[i]+"_count"))) {
1630  MultiFab temp_dat(mf[lev].boxArray(), mf[lev].DistributionMap(), 1, 0);
1631  temp_dat.setVal(0);
1632  if (particleData.HasSpecies(particles_namelist[i])) {
1633  particleData[particles_namelist[i]]->Increment(temp_dat, lev);
1634  }
1635  MultiFab::Copy(mf[lev], temp_dat, 0, mf_comp, 1, 0);
1636  mf_comp += 1;
1637  }
1638  }
1639  }
1640  }
1641 
1642  Vector<std::string> particle_mesh_plot_names(0);
1643  particleData.GetMeshPlotVarNames( particle_mesh_plot_names );
1644 
1645  for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
1646  std::string plot_var_name(particle_mesh_plot_names[i]);
1647  if (containerHasElement(plot_var_names, plot_var_name) ) {
1648  MultiFab temp_dat(mf[lev].boxArray(), mf[lev].DistributionMap(), 1, 1);
1649  temp_dat.setVal(0);
1650  particleData.GetMeshPlotVar(plot_var_name, temp_dat, *z_phys_nd[lev], lev);
1651  MultiFab::Copy(mf[lev], temp_dat, 0, mf_comp, 1, 0);
1652  mf_comp += 1;
1653  }
1654  }
1655 #endif
1656 
1657  {
1658  Vector<std::string> microphysics_plot_names;
1659  micro->GetPlotVarNames(microphysics_plot_names);
1660  for (auto& plot_name : microphysics_plot_names) {
1661  if (containerHasElement(plot_var_names, plot_name)) {
1662  MultiFab temp_dat(mf[lev].boxArray(), mf[lev].DistributionMap(), 1, 1);
1663  temp_dat.setVal(0);
1664  micro->GetPlotVar(plot_name, temp_dat, lev);
1665  MultiFab::Copy(mf[lev], temp_dat, 0, mf_comp, 1, 0);
1666  mf_comp += 1;
1667  }
1668  }
1669  }
1670  } // lev
1671 
1672  if (solverChoice.terrain_type == TerrainType::EB)
1673  {
1674  for (int lev = 0; lev <= finest_level; ++lev) {
1675  EB_set_covered(mf[lev], zero);
1676  }
1677  }
1678 
1679  // Fill terrain distortion MF (nu_nd)
1680  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
1681  for (int lev(0); lev <= finest_level; ++lev) {
1682  MultiFab::Copy(mf_nd[lev],*z_phys_nd[lev],0,2,1,0);
1683  Real dz = Geom()[lev].CellSizeArray()[2];
1684  for (MFIter mfi(mf_nd[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1685  const Box& bx = mfi.tilebox();
1686  Array4<Real> mf_arr = mf_nd[lev].array(mfi);
1687  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1688  {
1689  mf_arr(i,j,k,2) -= k * dz;
1690  });
1691  }
1692  }
1693  }
1694 
1695  std::string plotfilename;
1696  std::string plotfilenameU;
1697  std::string plotfilenameV;
1698  std::string plotfilenameW;
1699 
1700  if (which == 1) {
1702  const std::string dt_format = "%Y-%m-%d_%H:%M:%S"; // ISO 8601 standard
1703  plotfilename = plot3d_file_1+"_"+getTimestamp(start_time+t_new[0], dt_format,false);
1704  } else {
1705  plotfilename = Concatenate(plot3d_file_1, istep[0], file_name_digits);
1706  }
1707  plotfilenameU = Concatenate(plot3d_file_1+"U", istep[0], file_name_digits);
1708  plotfilenameV = Concatenate(plot3d_file_1+"V", istep[0], file_name_digits);
1709  plotfilenameW = Concatenate(plot3d_file_1+"W", istep[0], file_name_digits);
1710  } else if (which == 2) {
1712  const std::string dt_format = "%Y-%m-%d_%H:%M:%S"; // ISO 8601 standard
1713  plotfilename = plot3d_file_2+"_"+getTimestamp(start_time+t_new[0], dt_format,false);
1714  } else {
1715  plotfilename = Concatenate(plot3d_file_2, istep[0], file_name_digits);
1716  }
1717  plotfilenameU = Concatenate(plot3d_file_2+"U", istep[0], file_name_digits);
1718  plotfilenameV = Concatenate(plot3d_file_2+"V", istep[0], file_name_digits);
1719  plotfilenameW = Concatenate(plot3d_file_2+"W", istep[0], file_name_digits);
1720  }
1721 
1722  // LSM writes it's own data
1723  if (which==1 && plot_lsm) {
1724  lsm.Plot_Lsm_Data(t_new[0], istep, refRatio());
1725  }
1726 
1727 #ifdef ERF_USE_RRTMGP
1728  /*
1729  // write additional RRTMGP data
1730  // TODO: currently single level only
1731  if (which==1 && plot_rad) {
1732  rad[0]->writePlotfile(plot_file_1, t_new[0], istep[0]);
1733  }
1734  */
1735 #endif
1736 
1737  // Single level
1738  if (finest_level == 0)
1739  {
1740  if (plotfile_type == PlotFileType::Amrex)
1741  {
1742  Print() << "Writing native 3D plotfile " << plotfilename << "\n";
1743  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
1744  WriteMultiLevelPlotfileWithTerrain(plotfilename, finest_level+1,
1745  GetVecOfConstPtrs(mf),
1746  GetVecOfConstPtrs(mf_nd),
1747  varnames,
1748  Geom(), t_new[0], istep, refRatio());
1749  } else {
1750  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
1751  GetVecOfConstPtrs(mf),
1752  varnames,
1753  Geom(), t_new[0], istep, refRatio());
1754  }
1755  writeJobInfo(plotfilename);
1756 
1757  if (m_plot_face_vels) {
1758  Print() << "Writing face velocities" << std::endl;
1759  WriteMultiLevelPlotfile(plotfilenameU, finest_level+1,
1760  GetVecOfConstPtrs(mf_u),
1761  {"x_velocity_stag"},
1762  Geom(), t_new[0], istep, refRatio());
1763  WriteMultiLevelPlotfile(plotfilenameV, finest_level+1,
1764  GetVecOfConstPtrs(mf_v),
1765  {"y_velocity_stag"},
1766  Geom(), t_new[0], istep, refRatio());
1767  WriteMultiLevelPlotfile(plotfilenameW, finest_level+1,
1768  GetVecOfConstPtrs(mf_w),
1769  {"z_velocity_stag"},
1770  Geom(), t_new[0], istep, refRatio());
1771  }
1772 
1773 #ifdef ERF_USE_PARTICLES
1774  particleData.writePlotFile(plotfilename, z_phys_nd);
1775 #endif
1776 #ifdef ERF_USE_NETCDF
1777  } else if (plotfile_type == PlotFileType::Netcdf) {
1778  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
1779  int lev = 0;
1780  int l_which = 0;
1781  const Real* p_lo = geom[lev].ProbLo();
1782  const Real* p_hi = geom[lev].ProbHi();
1783  const auto dx = geom[lev].CellSize();
1784  writeNCPlotFile(lev, l_which, plotfilename, GetVecOfConstPtrs(mf), varnames, istep,
1785  {p_lo[0],p_lo[1],p_lo[2]},{p_hi[0],p_hi[1],p_hi[2]}, {dx[0],dx[1],dx[2]},
1786  geom[lev].Domain(), t_new[0], start_bdy_time, solverChoice, zlevels_stag[lev]);
1787 #endif
1788  } else {
1789  // Here we assume the plotfile_type is PlotFileType::None
1790  Print() << "Writing no 3D plotfile since plotfile_type is none" << std::endl;
1791  }
1792 
1793  } else { // Multilevel
1794 
1795  if (plotfile_type == PlotFileType::Amrex) {
1796 
1797  int lev0 = 0;
1798  int desired_ratio = std::max(std::max(ref_ratio[lev0][0],ref_ratio[lev0][1]),ref_ratio[lev0][2]);
1799  bool any_ratio_one = ( ( (ref_ratio[lev0][0] == 1) || (ref_ratio[lev0][1] == 1) ) ||
1800  (ref_ratio[lev0][2] == 1) );
1801  for (int lev = 1; lev < finest_level; lev++) {
1802  any_ratio_one = any_ratio_one ||
1803  ( ( (ref_ratio[lev][0] == 1) || (ref_ratio[lev][1] == 1) ) ||
1804  (ref_ratio[lev][2] == 1) );
1805  }
1806 
1807  if (any_ratio_one && m_expand_plotvars_to_unif_rr)
1808  {
1809  Vector<IntVect> r2(finest_level);
1810  Vector<Geometry> g2(finest_level+1);
1811  Vector<MultiFab> mf2(finest_level+1);
1812 
1813  mf2[0].define(grids[0], dmap[0], ncomp_mf, 0);
1814 
1815  // Copy level 0 as is
1816  MultiFab::Copy(mf2[0],mf[0],0,0,mf[0].nComp(),0);
1817 
1818  // Define a new multi-level array of Geometry's so that we pass the new "domain" at lev > 0
1819  Array<int,AMREX_SPACEDIM> periodicity =
1820  {Geom()[lev0].isPeriodic(0),Geom()[lev0].isPeriodic(1),Geom()[lev0].isPeriodic(2)};
1821  g2[lev0].define(Geom()[lev0].Domain(),&(Geom()[lev0].ProbDomain()),0,periodicity.data());
1822 
1823  r2[0] = IntVect(desired_ratio/ref_ratio[lev0][0],
1824  desired_ratio/ref_ratio[lev0][1],
1825  desired_ratio/ref_ratio[lev0][2]);
1826 
1827  for (int lev = 1; lev <= finest_level; ++lev) {
1828  if (lev > 1) {
1829  r2[lev-1][0] = r2[lev-2][0] * desired_ratio / ref_ratio[lev-1][0];
1830  r2[lev-1][1] = r2[lev-2][1] * desired_ratio / ref_ratio[lev-1][1];
1831  r2[lev-1][2] = r2[lev-2][2] * desired_ratio / ref_ratio[lev-1][2];
1832  }
1833 
1834  mf2[lev].define(refine(grids[lev],r2[lev-1]), dmap[lev], ncomp_mf, 0);
1835 
1836  // Set the new problem domain
1837  Box d2(Geom()[lev].Domain());
1838  d2.refine(r2[lev-1]);
1839 
1840  g2[lev].define(d2,&(Geom()[lev].ProbDomain()),0,periodicity.data());
1841  }
1842 
1843  //
1844  // We need to make a temporary that is the size of ncomp_mf
1845  // in order to not get an out of bounds error
1846  // even though the values will not be used
1847  //
1848  Vector<BCRec> temp_domain_bcs_type;
1849  temp_domain_bcs_type.resize(ncomp_mf);
1850 
1851  //
1852  // Do piecewise constant interpolation of mf into mf2
1853  //
1854  for (int lev = 1; lev <= finest_level; ++lev) {
1855  Interpolater* mapper_c = &pc_interp;
1856  InterpFromCoarseLevel(mf2[lev], t_new[lev], mf[lev],
1857  0, 0, ncomp_mf,
1858  geom[lev], g2[lev],
1860  r2[lev-1], mapper_c, temp_domain_bcs_type, 0);
1861  }
1862 
1863  // Define an effective ref_ratio which is isotropic to be passed into WriteMultiLevelPlotfile
1864  Vector<IntVect> rr(finest_level);
1865  for (int lev = 0; lev < finest_level; ++lev) {
1866  rr[lev] = IntVect(desired_ratio);
1867  }
1868 
1869  Print() << "Writing 3D plotfile " << plotfilename << "\n";
1870  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
1871  WriteMultiLevelPlotfileWithTerrain(plotfilename, finest_level+1,
1872  GetVecOfConstPtrs(mf2),
1873  GetVecOfConstPtrs(mf_nd),
1874  varnames,
1875  g2, t_new[0], istep, rr);
1876  } else {
1877  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
1878  GetVecOfConstPtrs(mf2), varnames,
1879  g2, t_new[0], istep, rr);
1880  }
1881 
1882  } else {
1883  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
1884  WriteMultiLevelPlotfileWithTerrain(plotfilename, finest_level+1,
1885  GetVecOfConstPtrs(mf),
1886  GetVecOfConstPtrs(mf_nd),
1887  varnames,
1888  geom, t_new[0], istep, ref_ratio);
1889  } else {
1890  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
1891  GetVecOfConstPtrs(mf), varnames,
1892  geom, t_new[0], istep, ref_ratio);
1893  }
1894  if (m_plot_face_vels) {
1895  Print() << "Writing face velocities" << std::endl;
1896  WriteMultiLevelPlotfile(plotfilenameU, finest_level+1,
1897  GetVecOfConstPtrs(mf_u),
1898  {"x_velocity_stag"},
1899  geom, t_new[0], istep, ref_ratio);
1900  WriteMultiLevelPlotfile(plotfilenameV, finest_level+1,
1901  GetVecOfConstPtrs(mf_v),
1902  {"y_velocity_stag"},
1903  geom, t_new[0], istep, ref_ratio);
1904  WriteMultiLevelPlotfile(plotfilenameW, finest_level+1,
1905  GetVecOfConstPtrs(mf_w),
1906  {"z_velocity_stag"},
1907  geom, t_new[0], istep, ref_ratio);
1908  }
1909  } // ref_ratio test
1910 
1911  writeJobInfo(plotfilename);
1912 
1913 #ifdef ERF_USE_PARTICLES
1914  particleData.writePlotFile(plotfilename, z_phys_nd);
1915 #endif
1916 
1917 #ifdef ERF_USE_NETCDF
1918  } else if (plotfile_type == PlotFileType::Netcdf) {
1919  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
1920  for (int lev = 0; lev <= finest_level; ++lev) {
1921  for (int which_box = 0; which_box < num_boxes_at_level[lev]; which_box++) {
1922  Box bounding_region = (lev == 0) ? geom[lev].Domain() : boxes_at_level[lev][which_box];
1923  const Real* p_lo = geom[lev].ProbLo();
1924  const Real* p_hi = geom[lev].ProbHi();
1925  const auto dx = geom[lev].CellSizeArray();
1926  writeNCPlotFile(lev, which_box, plotfilename, GetVecOfConstPtrs(mf), varnames, istep,
1927  {p_lo[0],p_lo[1],p_lo[2]},{p_hi[0],p_hi[1],p_hi[2]}, {dx[0],dx[1],dx[2]},
1928  bounding_region, t_new[0], start_bdy_time, solverChoice, zlevels_stag[lev]);
1929  }
1930  }
1931 #endif
1932  }
1933  } // end multi-level
1934 
1935  if (verbose > 0)
1936  {
1937  auto dPlotTime = amrex::second() - dPlotTime0;
1938  ParallelDescriptor::ReduceRealMax(dPlotTime,ParallelDescriptor::IOProcessorNumber());
1939  amrex::Print() << "3DPlotfile write time = " << dPlotTime << " seconds." << '\n';
1940  }
1941 }
constexpr amrex::Real PI
Definition: ERF_Constants.H:37
constexpr amrex::Real Cp_l
Definition: ERF_Constants.H:46
#define RhoQ4_comp
Definition: ERF_IndexDefines.H:45
#define RhoQ11_comp
Definition: ERF_IndexDefines.H:52
#define RhoQ9_comp
Definition: ERF_IndexDefines.H:50
#define RhoQ8_comp
Definition: ERF_IndexDefines.H:49
#define RhoQ7_comp
Definition: ERF_IndexDefines.H:48
#define RhoQ10_comp
Definition: ERF_IndexDefines.H:51
Real Ampl
Definition: ERF_InitCustomPert_MovingTerrain.H:4
Real H
Definition: ERF_InitCustomPert_MovingTerrain.H:7
Real wavelength
Definition: ERF_InitCustomPert_MovingTerrain.H:5
Real kp
Definition: ERF_InitCustomPert_MovingTerrain.H:8
void make_buoyancy(int lev, const Vector< MultiFab > &S_data, const MultiFab &S_prim, const MultiFab &qt, MultiFab &buoyancy, const Geometry geom, const SolverChoice &solverChoice, const MultiFab &base_state, const int n_qstate, const eb_ &ebfact, const int anelastic)
Definition: ERF_MakeBuoyancy.cpp:32
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:123
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw_cc(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:62
static PhysBCFunctNoOp null_bc_for_fill
Definition: ERF_Plotfile.cpp:11
void FillBdyCCVels(amrex::MultiFab &mf_cc_vel, amrex::Geometry &lev_geom)
Definition: ERF_FillBdyCCVels.cpp:11
void WriteMultiLevelPlotfileWithTerrain(const std::string &plotfilename, int nlevels, const amrex::Vector< const amrex::MultiFab * > &mf, const amrex::Vector< const amrex::MultiFab * > &mf_nd, const amrex::Vector< std::string > &varnames, const amrex::Vector< amrex::Geometry > &my_geom, amrex::Real time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &my_ref_ratio, const std::string &versionName="HyperCLaw-V1.1", const std::string &levelPrefix="Level_", const std::string &mfPrefix="Cell", const amrex::Vector< std::string > &extra_dirs=amrex::Vector< std::string >()) const
Definition: ERF_Plotfile.cpp:1944
void Plot_Lsm_Data(amrex::Real time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &ref_ratio)
Definition: ERF_LandSurface.H:138
@ Turb_lengthscale
Definition: ERF_IndexDefines.H:214
@ Mom_h
Definition: ERF_IndexDefines.H:204
@ Theta_h
Definition: ERF_IndexDefines.H:205
@ P
Definition: ERF_IndexDefines.H:164
void erf_derKE(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:342
void erf_dermoisttemp(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:266
void erf_dertemp(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:243
void erf_dersoundspeed(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:209
void erf_derlocalhelicity(const Box &bx, FArrayBox &derfab, int dcomp, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &geomdata, Real, const int *, const int)
Definition: ERF_Derive.cpp:616
void erf_dervorty(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &zcc_fab, const Geometry &geomdata, Real, const int *, const int)
Definition: ERF_Derive.cpp:386
void erf_dernull(const Box &, FArrayBox &, int, int, const FArrayBox &, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:189
void erf_dermucape(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &zcc_fab, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:746
void erf_derreflectivity(const Box &bx, FArrayBox &derfab, int dcomp, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:534
void erf_derprecipitable(const Box &bx, FArrayBox &derfab, int dcomp, int, const FArrayBox &datfab, const FArrayBox &zcc_fab, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:701
void erf_dervortx(const Box &bx, FArrayBox &derfab, int dcomp, int ncomp, const FArrayBox &datfab, const FArrayBox &zcc_fab, const Geometry &geomdata, Real, const int *, const int)
Definition: ERF_Derive.cpp:357
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19
Here is the call graph for this function:

◆ write_1D_profiles()

void ERF::write_1D_profiles ( double  time)

Writes 1-dimensional averaged quantities as profiles to output log files at the given time.

Parameters
timeCurrent time
18 {
19  BL_PROFILE("ERF::write_1D_profiles()");
20 
21  if (NumDataLogs() > 1)
22  {
23  // Define the 1d arrays we will need
24  Gpu::HostVector<Real> h_avg_u, h_avg_v, h_avg_w;
25  Gpu::HostVector<Real> h_avg_rho, h_avg_th, h_avg_ksgs, h_avg_Kmv, h_avg_Khv;
26  Gpu::HostVector<Real> h_avg_qv, h_avg_qc, h_avg_qr, h_avg_wqv, h_avg_wqc, h_avg_wqr, h_avg_qi, h_avg_qs, h_avg_qg;
27  Gpu::HostVector<Real> h_avg_wthv;
28  Gpu::HostVector<Real> h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth;
29  Gpu::HostVector<Real> h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww;
30  Gpu::HostVector<Real> h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw;
31  Gpu::HostVector<Real> h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw;
32  Gpu::HostVector<Real> h_avg_tau11, h_avg_tau12, h_avg_tau13, h_avg_tau22, h_avg_tau23, h_avg_tau33;
33  Gpu::HostVector<Real> h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx, h_avg_sgsdiss; // only output tau_{theta,w} and epsilon for now
34 
35  if (NumDataLogs() > 1) {
37  h_avg_u, h_avg_v, h_avg_w,
38  h_avg_rho, h_avg_th, h_avg_ksgs,
39  h_avg_Kmv, h_avg_Khv,
40  h_avg_qv, h_avg_qc, h_avg_qr,
41  h_avg_wqv, h_avg_wqc, h_avg_wqr,
42  h_avg_qi, h_avg_qs, h_avg_qg,
43  h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww,
44  h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth,
45  h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw,
46  h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw,
47  h_avg_wthv);
48  }
49 
50  if (NumDataLogs() > 3 && time > zero) {
51  derive_stress_profiles(h_avg_tau11, h_avg_tau12, h_avg_tau13,
52  h_avg_tau22, h_avg_tau23, h_avg_tau33,
53  h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx,
54  h_avg_sgsdiss);
55  }
56 
57  int hu_size = h_avg_u.size();
58 
59  auto const& dx = geom[0].CellSizeArray();
60  if (ParallelDescriptor::IOProcessor()) {
61  if (NumDataLogs() > 1) {
62  std::ostream& data_log1 = DataLog(1);
63  if (data_log1.good()) {
64  // Write the quantities at this time
65  for (int k = 0; k < hu_size; k++) {
66  Real z;
67  if (zlevels_stag[0].size() > 1) {
68  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
69  } else {
70  z = (k + myhalf)* dx[2];
71  }
72  data_log1 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
73  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
74  << h_avg_u[k] << " " << h_avg_v[k] << " " << h_avg_w[k] << " "
75  << h_avg_rho[k] << " " << h_avg_th[k] << " " << h_avg_ksgs[k] << " "
76  << h_avg_Kmv[k] << " " << h_avg_Khv[k] << " "
77  << h_avg_qv[k] << " " << h_avg_qc[k] << " " << h_avg_qr[k] << " "
78  << h_avg_qi[k] << " " << h_avg_qs[k] << " " << h_avg_qg[k]
79  << std::endl;
80  } // loop over z
81  } // if good
82  } // NumDataLogs
83 
84  if (NumDataLogs() > 2) {
85  std::ostream& data_log2 = DataLog(2);
86  if (data_log2.good()) {
87  // Write the perturbational quantities at this time
88  for (int k = 0; k < hu_size; k++) {
89  Real z;
90  if (zlevels_stag[0].size() > 1) {
91  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
92  } else {
93  z = (k + myhalf)* dx[2];
94  }
95  Real thv = h_avg_th[k] * (1 + Real(0.61)*h_avg_qv[k] - h_avg_qc[k] - h_avg_qr[k]);
96  data_log2 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
97  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
98  << h_avg_uu[k] - h_avg_u[k]*h_avg_u[k] << " "
99  << h_avg_uv[k] - h_avg_u[k]*h_avg_v[k] << " "
100  << h_avg_uw[k] - h_avg_u[k]*h_avg_w[k] << " "
101  << h_avg_vv[k] - h_avg_v[k]*h_avg_v[k] << " "
102  << h_avg_vw[k] - h_avg_v[k]*h_avg_w[k] << " "
103  << h_avg_ww[k] - h_avg_w[k]*h_avg_w[k] << " "
104  << h_avg_uth[k] - h_avg_u[k]*h_avg_th[k] << " "
105  << h_avg_vth[k] - h_avg_v[k]*h_avg_th[k] << " "
106  << h_avg_wth[k] - h_avg_w[k]*h_avg_th[k] << " "
107  << h_avg_thth[k] - h_avg_th[k]*h_avg_th[k] << " "
108  // Note: <u'_i u'_i u'_j> = <u_i u_i u_j>
109  // - <u_i u_i> * <u_j>
110  // - 2*<u_i> * <u_i u_j>
111  // + 2*<u_i>*<u_i> * <u_j>
112  << h_avg_uiuiu[k]
113  - (h_avg_uu[k] + h_avg_vv[k] + h_avg_ww[k])*h_avg_u[k]
114  - 2*(h_avg_u[k]*h_avg_uu[k] + h_avg_v[k]*h_avg_uv[k] + h_avg_w[k]*h_avg_uw[k])
115  + 2*(h_avg_u[k]*h_avg_u[k] + h_avg_v[k]*h_avg_v[k] + h_avg_w[k]*h_avg_w[k])*h_avg_u[k]
116  << " " // (u'_i u'_i)u'
117  << h_avg_uiuiv[k]
118  - (h_avg_uu[k] + h_avg_vv[k] + h_avg_ww[k])*h_avg_v[k]
119  - 2*(h_avg_u[k]*h_avg_uv[k] + h_avg_v[k]*h_avg_vv[k] + h_avg_w[k]*h_avg_vw[k])
120  + 2*(h_avg_u[k]*h_avg_u[k] + h_avg_v[k]*h_avg_v[k] + h_avg_w[k]*h_avg_w[k])*h_avg_v[k]
121  << " " // (u'_i u'_i)v'
122  << h_avg_uiuiw[k]
123  - (h_avg_uu[k] + h_avg_vv[k] + h_avg_ww[k])*h_avg_w[k]
124  - 2*(h_avg_u[k]*h_avg_uw[k] + h_avg_v[k]*h_avg_vw[k] + h_avg_w[k]*h_avg_ww[k])
125  + 2*(h_avg_u[k]*h_avg_u[k] + h_avg_v[k]*h_avg_v[k] + h_avg_w[k]*h_avg_w[k])*h_avg_w[k]
126  << " " // (u'_i u'_i)w'
127  << h_avg_pu[k] - h_avg_p[k]*h_avg_u[k] << " "
128  << h_avg_pv[k] - h_avg_p[k]*h_avg_v[k] << " "
129  << h_avg_pw[k] - h_avg_p[k]*h_avg_w[k] << " "
130  << h_avg_wqv[k] - h_avg_qv[k]*h_avg_w[k] << " "
131  << h_avg_wqc[k] - h_avg_qc[k]*h_avg_w[k] << " "
132  << h_avg_wqr[k] - h_avg_qr[k]*h_avg_w[k] << " "
133  << h_avg_wthv[k] - h_avg_w[k]*thv
134  << std::endl;
135  } // loop over z
136  } // if good
137  } // NumDataLogs
138 
139  if (NumDataLogs() > 3 && time > zero) {
140  std::ostream& data_log3 = DataLog(3);
141  if (data_log3.good()) {
142  // Write the average stresses
143  for (int k = 0; k < hu_size; k++) {
144  Real z;
145  if (zlevels_stag[0].size() > 1) {
146  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
147  } else {
148  z = (k + myhalf)* dx[2];
149  }
150  data_log3 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
151  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
152  << h_avg_tau11[k] << " " << h_avg_tau12[k] << " " << h_avg_tau13[k] << " "
153  << h_avg_tau22[k] << " " << h_avg_tau23[k] << " " << h_avg_tau33[k] << " "
154  << h_avg_sgshfx[k] << " "
155  << h_avg_sgsq1fx[k] << " " << h_avg_sgsq2fx[k] << " "
156  << h_avg_sgsdiss[k]
157  << std::endl;
158  } // loop over z
159  } // if good
160  } // if (NumDataLogs() > 3)
161  } // if IOProcessor
162  } // if (NumDataLogs() > 1)
163 }
void derive_diag_profiles(double time, amrex::Gpu::HostVector< amrex::Real > &h_avg_u, amrex::Gpu::HostVector< amrex::Real > &h_avg_v, amrex::Gpu::HostVector< amrex::Real > &h_avg_w, amrex::Gpu::HostVector< amrex::Real > &h_avg_rho, amrex::Gpu::HostVector< amrex::Real > &h_avg_th, amrex::Gpu::HostVector< amrex::Real > &h_avg_ksgs, amrex::Gpu::HostVector< amrex::Real > &h_avg_Kmv, amrex::Gpu::HostVector< amrex::Real > &h_avg_Khv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qc, amrex::Gpu::HostVector< amrex::Real > &h_avg_qr, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqv, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqc, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqr, amrex::Gpu::HostVector< amrex::Real > &h_avg_qi, amrex::Gpu::HostVector< amrex::Real > &h_avg_qs, amrex::Gpu::HostVector< amrex::Real > &h_avg_qg, amrex::Gpu::HostVector< amrex::Real > &h_avg_uu, amrex::Gpu::HostVector< amrex::Real > &h_avg_uv, amrex::Gpu::HostVector< amrex::Real > &h_avg_uw, amrex::Gpu::HostVector< amrex::Real > &h_avg_vv, amrex::Gpu::HostVector< amrex::Real > &h_avg_vw, amrex::Gpu::HostVector< amrex::Real > &h_avg_ww, amrex::Gpu::HostVector< amrex::Real > &h_avg_uth, amrex::Gpu::HostVector< amrex::Real > &h_avg_vth, amrex::Gpu::HostVector< amrex::Real > &h_avg_wth, amrex::Gpu::HostVector< amrex::Real > &h_avg_thth, amrex::Gpu::HostVector< amrex::Real > &h_avg_ku, amrex::Gpu::HostVector< amrex::Real > &h_avg_kv, amrex::Gpu::HostVector< amrex::Real > &h_avg_kw, amrex::Gpu::HostVector< amrex::Real > &h_avg_p, amrex::Gpu::HostVector< amrex::Real > &h_avg_pu, amrex::Gpu::HostVector< amrex::Real > &h_avg_pv, amrex::Gpu::HostVector< amrex::Real > &h_avg_pw, amrex::Gpu::HostVector< amrex::Real > &h_avg_wthv)
Definition: ERF_Write1DProfiles.cpp:190
void derive_stress_profiles(amrex::Gpu::HostVector< amrex::Real > &h_avg_tau11, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau12, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau13, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau22, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau23, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau33, amrex::Gpu::HostVector< amrex::Real > &h_avg_hfx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q1fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q2fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_diss)
Definition: ERF_Write1DProfiles.cpp:494

◆ write_1D_profiles_stag()

void ERF::write_1D_profiles_stag ( double  time)

Writes 1-dimensional averaged quantities as profiles to output log files at the given time.

Quantities are output at their native grid locations. Therefore, w and associated flux quantities <(•)'w'>, tau13, and tau23 (where '•' includes u, v, p, theta, ...) will be output at staggered heights (i.e., coincident with z faces) rather than cell-center heights to avoid performing additional averaging. Unstaggered (i.e., cell-centered) quantities are output alongside staggered quantities at the lower cell faces in the log file; these quantities will have a zero value at the big end, corresponding to k=Nz+one

The structure of file should follow ERF_Write1DProfiles.cpp

Parameters
timeCurrent time
26 {
27  BL_PROFILE("ERF::write_1D_profiles()");
28 
29  if (NumDataLogs() > 1)
30  {
31  // Define the 1d arrays we will need
32  Gpu::HostVector<Real> h_avg_u, h_avg_v, h_avg_w;
33  Gpu::HostVector<Real> h_avg_rho, h_avg_th, h_avg_ksgs, h_avg_Kmv, h_avg_Khv;
34  Gpu::HostVector<Real> h_avg_qv, h_avg_qc, h_avg_qr, h_avg_wqv, h_avg_wqc, h_avg_wqr, h_avg_qi, h_avg_qs, h_avg_qg;
35  Gpu::HostVector<Real> h_avg_wthv;
36  Gpu::HostVector<Real> h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth;
37  Gpu::HostVector<Real> h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww;
38  Gpu::HostVector<Real> h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw;
39  Gpu::HostVector<Real> h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw;
40  Gpu::HostVector<Real> h_avg_tau11, h_avg_tau12, h_avg_tau13, h_avg_tau22, h_avg_tau23, h_avg_tau33;
41  Gpu::HostVector<Real> h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx, h_avg_sgsdiss; // only output tau_{theta,w} and epsilon for now
42 
43  if (NumDataLogs() > 1) {
45  h_avg_u, h_avg_v, h_avg_w,
46  h_avg_rho, h_avg_th, h_avg_ksgs,
47  h_avg_Kmv, h_avg_Khv,
48  h_avg_qv, h_avg_qc, h_avg_qr,
49  h_avg_wqv, h_avg_wqc, h_avg_wqr,
50  h_avg_qi, h_avg_qs, h_avg_qg,
51  h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww,
52  h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth,
53  h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw,
54  h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw,
55  h_avg_wthv);
56  }
57 
58  if (NumDataLogs() > 3 && time > zero) {
59  derive_stress_profiles_stag(h_avg_tau11, h_avg_tau12, h_avg_tau13,
60  h_avg_tau22, h_avg_tau23, h_avg_tau33,
61  h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx,
62  h_avg_sgsdiss);
63  }
64 
65  int unstag_size = h_avg_w.size() - 1; // _un_staggered heights
66 
67  auto const& dx = geom[0].CellSizeArray();
68  if (ParallelDescriptor::IOProcessor()) {
69  if (NumDataLogs() > 1) {
70  std::ostream& data_log1 = DataLog(1);
71  if (data_log1.good()) {
72  // Write the quantities at this time
73  for (int k = 0; k < unstag_size; k++) {
74  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
75  data_log1 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
76  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
77  << h_avg_u[k] << " " << h_avg_v[k] << " " << h_avg_w[k] << " "
78  << h_avg_rho[k] << " " << h_avg_th[k] << " " << h_avg_ksgs[k] << " "
79  << h_avg_Kmv[k] << " " << h_avg_Khv[k] << " "
80  << h_avg_qv[k] << " " << h_avg_qc[k] << " " << h_avg_qr[k] << " "
81  << h_avg_qi[k] << " " << h_avg_qs[k] << " " << h_avg_qg[k]
82  << std::endl;
83  } // loop over z
84  // Write top face values
85  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
86  data_log1 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
87  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
88  << 0 << " " << 0 << " " << h_avg_w[unstag_size] << " "
89  << 0 << " " << 0 << " " << 0 << " " // rho, theta, ksgs
90  << 0 << " " << 0 << " " // Kmv, Khv
91  << 0 << " " << 0 << " " << 0 << " " // qv, qc, qr
92  << 0 << " " << 0 << " " << 0 // qi, qs, qg
93  << std::endl;
94  } // if good
95  } // NumDataLogs
96 
97  if (NumDataLogs() > 2) {
98  std::ostream& data_log2 = DataLog(2);
99  if (data_log2.good()) {
100  // Write the perturbational quantities at this time
101  // For surface values (k=0), assume w = uw = vw = ww = 0
102  Real w_cc = h_avg_w[1] / 2; // w at first cell center
103  Real uw_cc = h_avg_uw[1] / 2; // u*w at first cell center
104  Real vw_cc = h_avg_vw[1] / 2; // v*w at first cell center
105  Real ww_cc = h_avg_ww[1] / 2; // w*w at first cell center
106  data_log2 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
107  << std::setw(datwidth) << std::setprecision(datprecision) << 0 << " "
108  << h_avg_uu[0] - h_avg_u[0]*h_avg_u[0] << " " // u'u'
109  << h_avg_uv[0] - h_avg_u[0]*h_avg_v[0] << " " // u'v'
110  << 0 << " " // u'w'
111  << h_avg_vv[0] - h_avg_v[0]*h_avg_v[0] << " " // v'v'
112  << 0 << " " // v'w'
113  << 0 << " " // w'w'
114  << h_avg_uth[0] - h_avg_u[0]*h_avg_th[0] << " " // u'th'
115  << h_avg_vth[0] - h_avg_v[0]*h_avg_th[0] << " " // v'th'
116  << 0 << " " // w'th'
117  << h_avg_thth[0] - h_avg_th[0]*h_avg_th[0] << " " // th'th'
118  << h_avg_uiuiu[0]
119  - (h_avg_uu[0] + h_avg_vv[0] + ww_cc)*h_avg_u[0]
120  - 2*(h_avg_u[0]*h_avg_uu[0] + h_avg_v[0]*h_avg_uv[0] + w_cc*uw_cc)
121  + 2*(h_avg_u[0]*h_avg_u[0] + h_avg_v[0]*h_avg_v[0] + w_cc*w_cc)*h_avg_u[0]
122  << " " // (u'_i u'_i)u'
123  << h_avg_uiuiv[0]
124  - (h_avg_uu[0] + h_avg_vv[0] + ww_cc)*h_avg_v[0]
125  - 2*(h_avg_u[0]*h_avg_uv[0] + h_avg_v[0]*h_avg_vv[0] + w_cc*vw_cc)
126  + 2*(h_avg_u[0]*h_avg_u[0] + h_avg_v[0]*h_avg_v[0] + w_cc*w_cc)*h_avg_v[0]
127  << " " // (u'_i u'_i)v'
128  << 0 << " " // (u'_i u'_i)w'
129  << h_avg_pu[0] - h_avg_p[0]*h_avg_u[0] << " " // p'u'
130  << h_avg_pv[0] - h_avg_p[0]*h_avg_v[0] << " " // p'v'
131  << 0 << " " // p'w'
132  << 0 << " " // qv'w'
133  << 0 << " " // qc'w'
134  << 0 << " " // qr'w'
135  << 0 // thv'w'
136  << std::endl;
137 
138  // For internal values, interpolate scalar quantities to faces
139  for (int k = 1; k < unstag_size; k++) {
140  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
141  Real uface = myhalf*(h_avg_u[k] + h_avg_u[k-1]);
142  Real vface = myhalf*(h_avg_v[k] + h_avg_v[k-1]);
143  Real thface = myhalf*(h_avg_th[k] + h_avg_th[k-1]);
144  Real pface = myhalf*(h_avg_p[k] + h_avg_p[k-1]);
145  Real qvface = myhalf*(h_avg_qv[k] + h_avg_qv[k-1]);
146  Real qcface = myhalf*(h_avg_qc[k] + h_avg_qc[k-1]);
147  Real qrface = myhalf*(h_avg_qr[k] + h_avg_qr[k-1]);
148  Real uuface = myhalf*(h_avg_uu[k] + h_avg_uu[k-1]);
149  Real vvface = myhalf*(h_avg_vv[k] + h_avg_vv[k-1]);
150  Real thvface = thface * (1 + Real(0.61)*qvface - qcface - qrface);
151  w_cc = myhalf*(h_avg_w[k-1] + h_avg_w[k]);
152  uw_cc = myhalf*(h_avg_uw[k-1] + h_avg_uw[k]);
153  vw_cc = myhalf*(h_avg_vw[k-1] + h_avg_vw[k]);
154  ww_cc = myhalf*(h_avg_ww[k-1] + h_avg_ww[k]);
155  data_log2 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
156  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
157  << h_avg_uu[k] - h_avg_u[k]*h_avg_u[k] << " " // u'u'
158  << h_avg_uv[k] - h_avg_u[k]*h_avg_v[k] << " " // u'v'
159  << h_avg_uw[k] - uface*h_avg_w[k] << " " // u'w'
160  << h_avg_vv[k] - h_avg_v[k]*h_avg_v[k] << " " // v'v'
161  << h_avg_vw[k] - vface*h_avg_w[k] << " " // v'w'
162  << h_avg_ww[k] - h_avg_w[k]*h_avg_w[k] << " " // w'w'
163  << h_avg_uth[k] - h_avg_u[k]*h_avg_th[k] << " " // u'th'
164  << h_avg_vth[k] - h_avg_v[k]*h_avg_th[k] << " " // v'th'
165  << h_avg_wth[k] - h_avg_w[k]*thface << " " // w'th'
166  << h_avg_thth[k] - h_avg_th[k]*h_avg_th[k] << " " // th'th'
167  // Note: <u'_i u'_i u'_j> = <u_i u_i u_j>
168  // - <u_i u_i> * <u_j>
169  // - 2*<u_i> * <u_i u_j>
170  // + 2*<u_i>*<u_i> * <u_j>
171  << h_avg_uiuiu[k]
172  - (h_avg_uu[k] + h_avg_vv[k] + ww_cc)*h_avg_u[k]
173  - 2*(h_avg_u[k]*h_avg_uu[k] + h_avg_v[k]*h_avg_uv[k] + w_cc*uw_cc)
174  + 2*(h_avg_u[k]*h_avg_u[k] + h_avg_v[k]*h_avg_v[k] + w_cc*w_cc)*h_avg_u[k]
175  << " " // cell-centered (u'_i u'_i)u'
176  << h_avg_uiuiv[k]
177  - (h_avg_uu[k] + h_avg_vv[k] + ww_cc)*h_avg_v[k]
178  - 2*(h_avg_u[k]*h_avg_uv[k] + h_avg_v[k]*h_avg_vv[k] + w_cc*vw_cc)
179  + 2*(h_avg_u[k]*h_avg_u[k] + h_avg_v[k]*h_avg_v[k] + w_cc*w_cc)*h_avg_v[k]
180  << " " // cell-centered (u'_i u'_i)v'
181  << h_avg_uiuiw[k]
182  - (uuface + vvface + h_avg_ww[k])*h_avg_w[k]
183  - 2*(uface*h_avg_uw[k] + vface*h_avg_vw[k] + h_avg_w[k]*h_avg_ww[k])
184  + 2*(uface*uface + vface*vface + h_avg_w[k]*h_avg_w[k])*h_avg_w[k]
185  << " " // face-centered (u'_i u'_i)w'
186  << h_avg_pu[k] - h_avg_p[k]*h_avg_u[k] << " " // cell-centered p'u'
187  << h_avg_pv[k] - h_avg_p[k]*h_avg_v[k] << " " // cell-centered p'v'
188  << h_avg_pw[k] - pface*h_avg_w[k] << " " // face-centered p'w'
189  << h_avg_wqv[k] - qvface*h_avg_w[k] << " "
190  << h_avg_wqc[k] - qcface*h_avg_w[k] << " "
191  << h_avg_wqr[k] - qrface*h_avg_w[k] << " "
192  << h_avg_wthv[k] - thvface*h_avg_w[k]
193  << std::endl;
194  } // loop over z
195 
196  // Write top face values, extrapolating scalar quantities
197  const int k = unstag_size;
198  Real uface = Real(1.5)*h_avg_u[k-1] - myhalf*h_avg_u[k-2];
199  Real vface = Real(1.5)*h_avg_v[k-1] - myhalf*h_avg_v[k-2];
200  Real thface = Real(1.5)*h_avg_th[k-1] - myhalf*h_avg_th[k-2];
201  Real pface = Real(1.5)*h_avg_p[k-1] - myhalf*h_avg_p[k-2];
202  Real qvface = Real(1.5)*h_avg_qv[k-1] - myhalf*h_avg_qv[k-2];
203  Real qcface = Real(1.5)*h_avg_qc[k-1] - myhalf*h_avg_qc[k-2];
204  Real qrface = Real(1.5)*h_avg_qr[k-1] - myhalf*h_avg_qr[k-2];
205  Real uuface = Real(1.5)*h_avg_uu[k-1] - myhalf*h_avg_uu[k-2];
206  Real vvface = Real(1.5)*h_avg_vv[k-1] - myhalf*h_avg_vv[k-2];
207  Real thvface = thface * (1 + Real(0.61)*qvface - qcface - qrface);
208  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
209  data_log2 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
210  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
211  << 0 << " " // u'u'
212  << 0 << " " // u'v'
213  << h_avg_uw[k] - uface*h_avg_w[k] << " " // u'w'
214  << 0 << " " // v'v'
215  << h_avg_vw[k] - vface*h_avg_w[k] << " " // v'w'
216  << h_avg_ww[k] - h_avg_w[k]*h_avg_w[k] << " " // w'w'
217  << 0 << " " // u'th'
218  << 0 << " " // v'th'
219  << h_avg_wth[k] - thface*h_avg_w[k] << " " // w'th'
220  << 0 << " " // th'th'
221  << 0 << " " // (u'_i u'_i)u'
222  << 0 << " " // (u'_i u'_i)v'
223  << h_avg_uiuiw[k]
224  - (uuface + vvface + h_avg_ww[k])*h_avg_w[k]
225  - 2*(uface*h_avg_uw[k] + vface*h_avg_vw[k] + h_avg_w[k]*h_avg_ww[k])
226  + 2*(uface*uface + vface*vface + h_avg_w[k]*h_avg_w[k])*h_avg_w[k]
227  << " " // (u'_i u'_i)w'
228  << 0 << " " // pu'
229  << 0 << " " // pv'
230  << h_avg_pw[k] - pface*h_avg_w[k] << " " // pw'
231  << h_avg_wqv[k] - qvface*h_avg_w[k] << " "
232  << h_avg_wqc[k] - qcface*h_avg_w[k] << " "
233  << h_avg_wqr[k] - qrface*h_avg_w[k] << " "
234  << h_avg_wthv[k] - thvface*h_avg_w[k]
235  << std::endl;
236  } // if good
237  } // NumDataLogs
238 
239  if (NumDataLogs() > 3 && time > zero) {
240  std::ostream& data_log3 = DataLog(3);
241  if (data_log3.good()) {
242  // Write the average stresses
243  for (int k = 0; k < unstag_size; k++) {
244  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
245  data_log3 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
246  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
247  << h_avg_tau11[k] << " " << h_avg_tau12[k] << " " << h_avg_tau13[k] << " "
248  << h_avg_tau22[k] << " " << h_avg_tau23[k] << " " << h_avg_tau33[k] << " "
249  << h_avg_sgshfx[k] << " "
250  << h_avg_sgsq1fx[k] << " " << h_avg_sgsq2fx[k] << " "
251  << h_avg_sgsdiss[k]
252  << std::endl;
253  } // loop over z
254  // Write top face values
255  Real NANval = zero;
256  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
257  data_log3 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
258  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
259  << NANval << " " << NANval << " " << h_avg_tau13[unstag_size] << " "
260  << NANval << " " << h_avg_tau23[unstag_size] << " " << NANval << " "
261  << h_avg_sgshfx[unstag_size] << " "
262  << h_avg_sgsq1fx[unstag_size] << " " << h_avg_sgsq2fx[unstag_size] << " "
263  << NANval
264  << std::endl;
265  } // if good
266  } // if (NumDataLogs() > 3)
267  } // if IOProcessor
268  } // if (NumDataLogs() > 1)
269 }
void derive_diag_profiles_stag(double time, amrex::Gpu::HostVector< amrex::Real > &h_avg_u, amrex::Gpu::HostVector< amrex::Real > &h_avg_v, amrex::Gpu::HostVector< amrex::Real > &h_avg_w, amrex::Gpu::HostVector< amrex::Real > &h_avg_rho, amrex::Gpu::HostVector< amrex::Real > &h_avg_th, amrex::Gpu::HostVector< amrex::Real > &h_avg_ksgs, amrex::Gpu::HostVector< amrex::Real > &h_avg_Kmv, amrex::Gpu::HostVector< amrex::Real > &h_avg_Khv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qv, amrex::Gpu::HostVector< amrex::Real > &h_avg_qc, amrex::Gpu::HostVector< amrex::Real > &h_avg_qr, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqv, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqc, amrex::Gpu::HostVector< amrex::Real > &h_avg_wqr, amrex::Gpu::HostVector< amrex::Real > &h_avg_qi, amrex::Gpu::HostVector< amrex::Real > &h_avg_qs, amrex::Gpu::HostVector< amrex::Real > &h_avg_qg, amrex::Gpu::HostVector< amrex::Real > &h_avg_uu, amrex::Gpu::HostVector< amrex::Real > &h_avg_uv, amrex::Gpu::HostVector< amrex::Real > &h_avg_uw, amrex::Gpu::HostVector< amrex::Real > &h_avg_vv, amrex::Gpu::HostVector< amrex::Real > &h_avg_vw, amrex::Gpu::HostVector< amrex::Real > &h_avg_ww, amrex::Gpu::HostVector< amrex::Real > &h_avg_uth, amrex::Gpu::HostVector< amrex::Real > &h_avg_vth, amrex::Gpu::HostVector< amrex::Real > &h_avg_wth, amrex::Gpu::HostVector< amrex::Real > &h_avg_thth, amrex::Gpu::HostVector< amrex::Real > &h_avg_ku, amrex::Gpu::HostVector< amrex::Real > &h_avg_kv, amrex::Gpu::HostVector< amrex::Real > &h_avg_kw, amrex::Gpu::HostVector< amrex::Real > &h_avg_p, amrex::Gpu::HostVector< amrex::Real > &h_avg_pu, amrex::Gpu::HostVector< amrex::Real > &h_avg_pv, amrex::Gpu::HostVector< amrex::Real > &h_avg_pw, amrex::Gpu::HostVector< amrex::Real > &h_avg_wthv)
Definition: ERF_Write1DProfiles_stag.cpp:296
void derive_stress_profiles_stag(amrex::Gpu::HostVector< amrex::Real > &h_avg_tau11, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau12, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau13, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau22, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau23, amrex::Gpu::HostVector< amrex::Real > &h_avg_tau33, amrex::Gpu::HostVector< amrex::Real > &h_avg_hfx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q1fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_q2fx3, amrex::Gpu::HostVector< amrex::Real > &h_avg_diss)
Definition: ERF_Write1DProfiles_stag.cpp:624

◆ WriteAtFinalTime()

void ERF::WriteAtFinalTime ( )
279 {
280  // Write plotfiles at final time
284  }
288  }
292  }
296  }
297 
298  for (int i = 0; i < m_subvol_int.size(); i++) {
299  if ( (m_subvol_int[i] > 0 || m_subvol_per[i] > zero) && istep[0] > last_subvol_step[i]) {
301  if (m_subvol_per[i] > zero) {last_subvol_time[i] += m_subvol_per[i];}
302  }
303  }
304 
305  if ( (m_check_int > 0 || m_check_per > zero) && istep[0] > last_check_file_step) {
308  }
309 }

◆ WriteAtIntermediateTime()

void ERF::WriteAtIntermediateTime ( int  nstep,
double  time 
)
236 {
237  if (writeNow(cur_time, step+1, m_plot3d_int_1, m_plot3d_per_1, dt[0], last_plot3d_file_time_1)) {
238  last_plot3d_file_step_1 = step+1;
240  for (int lev = 0; lev <= finest_level; ++lev) {lsm.Plot(lev, step+1);}
242  }
243  if (writeNow(cur_time, step+1, m_plot3d_int_2, m_plot3d_per_2, dt[0], last_plot3d_file_time_2)) {
244  last_plot3d_file_step_2 = step+1;
246  for (int lev = 0; lev <= finest_level; ++lev) {lsm.Plot(lev, step+1);}
248  }
249 
250  if (writeNow(cur_time, step+1, m_plot2d_int_1, m_plot2d_per_1, dt[0], last_plot2d_file_time_1)) {
251  last_plot2d_file_step_1 = step+1;
254  }
255 
256  if (writeNow(cur_time, step+1, m_plot2d_int_2, m_plot2d_per_2, dt[0], last_plot2d_file_time_2)) {
257  last_plot2d_file_step_2 = step+1;
260  }
261 
262  for (int i = 0; i < m_subvol_int.size(); i++) {
263  if (writeNow(cur_time, step+1, m_subvol_int[i], m_subvol_per[i], dt[0], last_subvol_time[i])) {
264  last_subvol_step[i] = step+1;
266  if (m_subvol_per[i] > zero) {last_subvol_time[i] += m_subvol_per[i];}
267  }
268  }
269 
270  if (writeNow(cur_time, step+1, m_check_int, m_check_per, dt[0], last_check_file_time)) {
271  last_check_file_step = step+1;
274  }
275 }
bool writeNow(double cur_time, const int nstep, const int plot_int, const amrex::Real plot_per, const amrex::Real dt_0, amrex::Real &last_file_time)
Definition: ERF.cpp:2769
void Plot(const int &lev, const int &nstep)
Definition: ERF_LandSurface.H:74

Referenced by EvolveOneStep().

Here is the caller graph for this function:

◆ writeBuildInfo()

void ERF::writeBuildInfo ( std::ostream &  os)
static
144 {
145  std::string PrettyLine = std::string(78, '=') + "\n";
146  std::string OtherLine = std::string(78, '-') + "\n";
147  std::string SkipSpace = std::string(8, ' ');
148 
149  // build information
150  os << PrettyLine;
151  os << " ERF Build Information\n";
152  os << PrettyLine;
153 
154  os << "build date: " << buildInfoGetBuildDate() << "\n";
155  os << "build machine: " << buildInfoGetBuildMachine() << "\n";
156  os << "build dir: " << buildInfoGetBuildDir() << "\n";
157  os << "AMReX dir: " << buildInfoGetAMReXDir() << "\n";
158 
159  os << "\n";
160 
161  os << "COMP: " << buildInfoGetComp() << "\n";
162  os << "COMP version: " << buildInfoGetCompVersion() << "\n";
163 
164  os << "C++ compiler: " << buildInfoGetCXXName() << "\n";
165  os << "C++ flags: " << buildInfoGetCXXFlags() << "\n";
166 
167  os << "\n";
168 
169  os << "Link flags: " << buildInfoGetLinkFlags() << "\n";
170  os << "Libraries: " << buildInfoGetLibraries() << "\n";
171 
172  os << "\n";
173 
174  for (int n = 1; n <= buildInfoGetNumModules(); n++) {
175  os << buildInfoGetModuleName(n) << ": "
176  << buildInfoGetModuleVal(n) << "\n";
177  }
178 
179  os << "\n";
180  const char* githash1 = buildInfoGetGitHash(1);
181  const char* githash2 = buildInfoGetGitHash(2);
182  if (strlen(githash1) > 0) {
183  os << "ERF git hash: " << githash1 << "\n";
184  }
185  if (strlen(githash2) > 0) {
186  os << "AMReX git hash: " << githash2 << "\n";
187  }
188 
189  const char* buildgithash = buildInfoGetBuildGitHash();
190  const char* buildgitname = buildInfoGetBuildGitName();
191  if (strlen(buildgithash) > 0) {
192  os << buildgitname << " git hash: " << buildgithash << "\n";
193  }
194 
195  os << "\n";
196  os << " ERF Compile time variables: \n";
197 
198  os << "\n";
199  os << " ERF Defines: \n";
200 #ifdef _OPENMP
201  os << std::setw(35) << std::left << "_OPENMP " << std::setw(6) << "ON"
202  << std::endl;
203 #else
204  os << std::setw(35) << std::left << "_OPENMP " << std::setw(6) << "OFF"
205  << std::endl;
206 #endif
207 
208 #ifdef MPI_VERSION
209  os << std::setw(35) << std::left << "MPI_VERSION " << std::setw(6)
210  << MPI_VERSION << std::endl;
211 #else
212  os << std::setw(35) << std::left << "MPI_VERSION " << std::setw(6)
213  << "UNDEFINED" << std::endl;
214 #endif
215 
216 #ifdef MPI_SUBVERSION
217  os << std::setw(35) << std::left << "MPI_SUBVERSION " << std::setw(6)
218  << MPI_SUBVERSION << std::endl;
219 #else
220  os << std::setw(35) << std::left << "MPI_SUBVERSION " << std::setw(6)
221  << "UNDEFINED" << std::endl;
222 #endif
223 
224  os << "\n\n";
225 }

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WriteCheckpointFile()

void ERF::WriteCheckpointFile ( ) const

ERF function for writing a checkpoint file.

28 {
29  auto dCheckTime0 = amrex::second();
30 
31  // chk00010 write a checkpoint file with this root directory
32  // chk00010/Header this contains information you need to save (e.g., finest_level, t_new, etc.) and also
33  // the BoxArrays at each level
34  // chk00010/Level_0/
35  // chk00010/Level_1/
36  // etc. these subdirectories will hold the MultiFab data at each level of refinement
37 
38  // checkpoint file name, e.g., chk00010
39  const std::string& checkpointname = Concatenate(check_file,istep[0],file_name_digits);
40 
41  Print() << "Writing native checkpoint " << checkpointname << "\n";
42 
43  const int nlevels = finest_level+1;
44 
45  // ---- prebuild a hierarchy of directories
46  // ---- dirName is built first. if dirName exists, it is renamed. then build
47  // ---- dirName/subDirPrefix_0 .. dirName/subDirPrefix_nlevels-1
48  // ---- if callBarrier is true, call ParallelDescriptor::Barrier()
49  // ---- after all directories are built
50  // ---- ParallelDescriptor::IOProcessor() creates the directories
51  PreBuildDirectorHierarchy(checkpointname, "Level_", nlevels, true);
52 
53  int ncomp_cons = vars_new[0][Vars::cons].nComp();
54 
55  // write Header file
56  if (ParallelDescriptor::IOProcessor()) {
57 
58  std::string HeaderFileName(checkpointname + "/Header");
59  VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
60  std::ofstream HeaderFile;
61  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
62  HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
63  std::ofstream::trunc |
64  std::ofstream::binary);
65  if(! HeaderFile.good()) {
66  FileOpenFailed(HeaderFileName);
67  }
68 
69  HeaderFile.precision(17);
70 
71  // write out title line
72  HeaderFile << "Checkpoint file for ERF\n";
73 
74  // write out finest_level
75  HeaderFile << finest_level << "\n";
76 
77  // write the number of components
78  // for each variable we store
79 
80  // conservative, cell-centered vars
81  HeaderFile << ncomp_cons << "\n";
82 
83  // x-velocity on faces
84  HeaderFile << 1 << "\n";
85 
86  // y-velocity on faces
87  HeaderFile << 1 << "\n";
88 
89  // z-velocity on faces
90  HeaderFile << 1 << "\n";
91 
92  // write out array of istep
93  for (int i = 0; i < istep.size(); ++i) {
94  HeaderFile << istep[i] << " ";
95  }
96  HeaderFile << "\n";
97 
98  // write out array of dt
99  for (int i = 0; i < dt.size(); ++i) {
100  HeaderFile << dt[i] << " ";
101  }
102  HeaderFile << "\n";
103 
104  // write out array of t_new
105  for (int i = 0; i < t_new.size(); ++i) {
106  HeaderFile << t_new[i] << " ";
107  }
108  HeaderFile << "\n";
109 
110  // write the BoxArray at each level
111  for (int lev = 0; lev <= finest_level; ++lev) {
112  boxArray(lev).writeOn(HeaderFile);
113  HeaderFile << '\n';
114  }
115 
116  // Write separate file that tells how many components we have of the base state
117  std::string BaseStateFileName(checkpointname + "/num_base_state_comps");
118  std::ofstream BaseStateFile;
119  BaseStateFile.open(BaseStateFileName.c_str(), std::ofstream::out |
120  std::ofstream::trunc |
121  std::ofstream::binary);
122  if(! BaseStateFile.good()) {
123  FileOpenFailed(BaseStateFileName);
124  } else {
125  // write out number of components in base state
126  BaseStateFile << BaseState::num_comps << "\n";
127  BaseStateFile << base_state[0].nGrowVect() << "\n";
128  }
129 
130  // Persist the LSM scalar step counter (e.g. NoahMP itimestep). Without
131  // this, restart resets the substep schedule, which changes the LSM->MOST
132  // flux firing times and produces a non-bitwise trajectory vs. cold start.
133  if (solverChoice.lsm_type != LandSurfaceType::None) {
134  std::string LsmStepFileName(checkpointname + "/lsm_step");
135  std::ofstream LsmStepFile;
136  LsmStepFile.open(LsmStepFileName.c_str(), std::ofstream::out |
137  std::ofstream::trunc |
138  std::ofstream::binary);
139  if(! LsmStepFile.good()) {
140  FileOpenFailed(LsmStepFileName);
141  } else {
142  for (int lev = 0; lev <= finest_level; ++lev) {
143  LsmStepFile << lsm.Get_LSM_Step(lev) << " ";
144  }
145  LsmStepFile << "\n";
146  }
147  }
148  }
149 
150  // write the MultiFab data to, e.g., chk00010/Level_0/
151  // Here we make copies of the MultiFab with no ghost cells
152  for (int lev = 0; lev <= finest_level; ++lev)
153  {
154  MultiFab cons(grids[lev],dmap[lev],ncomp_cons,0);
155  MultiFab::Copy(cons,vars_new[lev][Vars::cons],0,0,ncomp_cons,0);
156  VisMF::Write(cons, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Cell"));
157 
158  MultiFab xvel(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
159  MultiFab::Copy(xvel,vars_new[lev][Vars::xvel],0,0,1,0);
160  VisMF::Write(xvel, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "XFace"));
161 
162  MultiFab yvel(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
163  MultiFab::Copy(yvel,vars_new[lev][Vars::yvel],0,0,1,0);
164  VisMF::Write(yvel, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "YFace"));
165 
166  MultiFab zvel(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
167  MultiFab::Copy(zvel,vars_new[lev][Vars::zvel],0,0,1,0);
168  VisMF::Write(zvel, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "ZFace"));
169 
170  if (solverChoice.anelastic[lev] == 1) {
171  MultiFab ppinc(grids[lev],dmap[lev],1,0);
172  MultiFab::Copy(ppinc,pp_inc[lev],0,0,1,0);
173  VisMF::Write(ppinc, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "PP_Inc"));
174 
175  MultiFab gpx(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
176  MultiFab::Copy(gpx,gradp[lev][GpVars::gpx],0,0,1,0);
177  VisMF::Write(gpx, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Gpx"));
178 
179  MultiFab gpy(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
180  MultiFab::Copy(gpy,gradp[lev][GpVars::gpy],0,0,1,0);
181  VisMF::Write(gpy, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Gpy"));
182 
183  MultiFab gpz(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
184  MultiFab::Copy(gpz,gradp[lev][GpVars::gpz],0,0,1,0);
185  VisMF::Write(gpz, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Gpz"));
186  }
187 
188  // Note that we write the ghost cells of the base state (unlike above)
189  IntVect ng_base = base_state[lev].nGrowVect();
190  int ncomp_base = base_state[lev].nComp();
191  MultiFab base(grids[lev],dmap[lev],ncomp_base,ng_base);
192  MultiFab::Copy(base,base_state[lev],0,0,ncomp_base,ng_base);
193  VisMF::Write(base, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "BaseState"));
194 
195  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
196  // Note that we also write the ghost cells of z_phys_nd
197  IntVect ng = z_phys_nd[lev]->nGrowVect();
198  MultiFab z_height(convert(grids[lev],IntVect(1,1,1)),dmap[lev],1,ng);
199  MultiFab::Copy(z_height,*z_phys_nd[lev],0,0,1,ng);
200  VisMF::Write(z_height, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Z_Phys_nd"));
201  }
202 
203  // We must read and write qmoist with ghost cells because we don't directly impose BCs on these vars
204  // Write the moisture model restart variables
205  std::vector<int> qmoist_indices;
206  std::vector<std::string> qmoist_names;
207  micro->Get_Qmoist_Restart_Vars(lev, solverChoice, qmoist_indices, qmoist_names);
208  int qmoist_nvar = static_cast<int>(qmoist_indices.size());
209  for (int var = 0; var < qmoist_nvar; var++) {
210  const int ncomp = 1;
211  IntVect ng_moist = qmoist[lev][qmoist_indices[var]]->nGrowVect();
212  MultiFab moist_vars(grids[lev],dmap[lev],ncomp,ng_moist);
213  MultiFab::Copy(moist_vars,*(qmoist[lev][qmoist_indices[var]]),0,0,ncomp,ng_moist);
214  VisMF::Write(moist_vars, amrex::MultiFabFileFullPrefix(lev, checkpointname, "Level_", qmoist_names[var]));
215  }
216 
217 #if defined(ERF_USE_WINDFARM)
218  if(solverChoice.windfarm_type == WindFarmType::Fitch or
219  solverChoice.windfarm_type == WindFarmType::EWP or
220  solverChoice.windfarm_type == WindFarmType::SimpleAD){
221  IntVect ng_turb = Nturb[lev].nGrowVect();
222  MultiFab mf_Nturb(grids[lev],dmap[lev],1,ng_turb);
223  MultiFab::Copy(mf_Nturb,Nturb[lev],0,0,1,ng_turb);
224  VisMF::Write(mf_Nturb, amrex::MultiFabFileFullPrefix(lev, checkpointname, "Level_", "NumTurb"));
225  }
226 #endif
227 
228  // Write the LSM data
229  if (solverChoice.lsm_type != LandSurfaceType::None) {
230  for (int ivar(0); ivar<lsm_data[lev].size(); ++ivar) {
231  BoxArray ba = lsm_data[lev][ivar]->boxArray();
232  DistributionMapping dm = lsm_data[lev][ivar]->DistributionMap();
233  IntVect ng = lsm_data[lev][ivar]->nGrowVect();
234  int nvar = lsm_data[lev][ivar]->nComp();
235  MultiFab lsm_vars(ba,dm,nvar,ng);
236  MultiFab::Copy(lsm_vars,*(lsm_data[lev][ivar]),0,0,nvar,ng);
237  VisMF::Write(lsm_vars, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LsmData" + std::to_string(ivar)));
238  }
239  for (int iflux(0); iflux<lsm_flux[lev].size(); ++iflux) {
240  BoxArray ba = lsm_flux[lev][iflux]->boxArray();
241  DistributionMapping dm = lsm_flux[lev][iflux]->DistributionMap();
242  IntVect ng = lsm_flux[lev][iflux]->nGrowVect();
243  int nvar = lsm_flux[lev][iflux]->nComp();
244  MultiFab lsm_vars(ba,dm,nvar,ng);
245  MultiFab::Copy(lsm_vars,*(lsm_flux[lev][iflux]),0,0,nvar,ng);
246  VisMF::Write(lsm_vars, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LsmFlux" + std::to_string(iflux)));
247  }
248 
249  // Write the full LSM prognostic state (e.g. NoahMP soil/snow/canopy)
250  // to chk*/noahmp_restart/Level_<lev>.nc so a restart reproduces a
251  // cold-start trajectory bitwise (issue #3255). The LSM model writes
252  // its blocks collectively; no-op for models without such state.
253  // Create the subdir on rank 0 and barrier before the collective open
254  // so no rank races ahead of the directory existing.
255  std::string LsmRestartDir(checkpointname + "/noahmp_restart");
256  if (ParallelDescriptor::IOProcessor()) {
257  amrex::UtilCreateDirectory(LsmRestartDir, 0755);
258  }
259  ParallelDescriptor::Barrier();
260  lsm.Write_Lsm_Restart(lev, LsmRestartDir);
261  }
262 
263  // Write the radiation heating rates
264  if ((solverChoice.rad_type != RadiationType::None) && (qheating_rates[lev])) {
265  int nrad = qheating_rates[lev]->nComp();
266  MultiFab mf_rad(grids[lev],dmap[lev],nrad,0);
267  MultiFab::Copy(mf_rad,*qheating_rates[lev],0,0,nrad,0);
268  VisMF::Write(mf_rad, amrex::MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Qrad"));
269  }
270 
271  IntVect ng = mapfac[lev][MapFacType::m_x]->nGrowVect();
272  MultiFab mf_m(ba2d[lev],dmap[lev],1,ng);
273  MultiFab::Copy(mf_m,*mapfac[lev][MapFacType::m_x],0,0,1,ng);
274  VisMF::Write(mf_m, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_mx"));
275 
276 #if 0
278  MultiFab::Copy(mf_m,*mapfac[lev][MapFacType::m_y],0,0,1,ng);
279  VisMF::Write(mf_m, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_my"));
280  }
281 #endif
282 
283  ng = mapfac[lev][MapFacType::u_x]->nGrowVect();
284  MultiFab mf_u(convert(ba2d[lev],IntVect(1,0,0)),dmap[lev],1,ng);
285  MultiFab::Copy(mf_u,*mapfac[lev][MapFacType::u_x],0,0,1,ng);
286  VisMF::Write(mf_u, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_ux"));
287 
288 #if 0
290  MultiFab::Copy(mf_u,*mapfac[lev][MapFacType::u_y],0,0,1,ng);
291  VisMF::Write(mf_u, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_uy"));
292  }
293 #endif
294 
295  ng = mapfac[lev][MapFacType::v_x]->nGrowVect();
296  MultiFab mf_v(convert(ba2d[lev],IntVect(0,1,0)),dmap[lev],1,ng);
297  MultiFab::Copy(mf_v,*mapfac[lev][MapFacType::v_x],0,0,1,ng);
298  VisMF::Write(mf_v, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_vx"));
299 
300 #if 0
302  MultiFab::Copy(mf_v,*mapfac[lev][MapFacType::v_y],0,0,1,ng);
303  VisMF::Write(mf_v, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_vy"));
304  }
305 #endif
306 
307  if (m_SurfaceLayer) {
308  amrex::Print() << "Writing SurfaceLayer variables at level " << lev << std::endl;
309  ng = IntVect(1,1,0);
310  MultiFab m_var(ba2d[lev],dmap[lev],1,ng);
311  MultiFab* src = nullptr;
312 
313  // U*
314  src = m_SurfaceLayer->get_u_star(lev);
315  MultiFab::Copy(m_var,*src,0,0,1,ng);
316  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Ustar"));
317 
318  // W*
319  src = m_SurfaceLayer->get_w_star(lev);
320  MultiFab::Copy(m_var,*src,0,0,1,ng);
321  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Wstar"));
322 
323  // T*
324  src = m_SurfaceLayer->get_t_star(lev);
325  MultiFab::Copy(m_var,*src,0,0,1,ng);
326  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Tstar"));
327 
328  // Q*
329  src = m_SurfaceLayer->get_q_star(lev);
330  MultiFab::Copy(m_var,*src,0,0,1,ng);
331  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Qstar"));
332 
333  // Olen
334  src = m_SurfaceLayer->get_olen(lev);
335  MultiFab::Copy(m_var,*src,0,0,1,ng);
336  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Olen"));
337 
338  // Qsurf
339  src = m_SurfaceLayer->get_q_surf(lev);
340  MultiFab::Copy(m_var,*src,0,0,1,ng);
341  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Qsurf"));
342 
343  // PBLH
344  src = m_SurfaceLayer->get_pblh(lev);
345  MultiFab::Copy(m_var,*src,0,0,1,ng);
346  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "PBLH"));
347 
348  // Z0
349  src = m_SurfaceLayer->get_z0(lev);
350  MultiFab::Copy(m_var,*src,0,0,1,ng);
351  VisMF::Write(m_var, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Z0"));
352  }
353 
354  if (sst_lev[lev][0]) {
355  int ntimes = 1;
356  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
357  MultiFab sst_at_t(ba2d[lev],dmap[lev],1,ng);
358  for (int nt(0); nt<ntimes; ++nt) {
359  MultiFab::Copy(sst_at_t,*sst_lev[lev][nt],0,0,1,ng);
360  VisMF::Write(sst_at_t, MultiFabFileFullPrefix(lev, checkpointname, "Level_",
361  "SST_" + std::to_string(nt)));
362  }
363  }
364 
365  if (tsk_lev[lev][0]) {
366  int ntimes = 1;
367  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
368  MultiFab tsk_at_t(ba2d[lev],dmap[lev],1,ng);
369  for (int nt(0); nt<ntimes; ++nt) {
370  MultiFab::Copy(tsk_at_t,*tsk_lev[lev][nt],0,0,1,ng);
371  VisMF::Write(tsk_at_t, MultiFabFileFullPrefix(lev, checkpointname, "Level_",
372  "TSK_" + std::to_string(nt)));
373  }
374  }
375 
376  {
377  int ntimes = 1;
378  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
379  MultiFab lmask_at_t(ba2d[lev],dmap[lev],1,ng);
380  for (int nt(0); nt<ntimes; ++nt) {
381  for (MFIter mfi(lmask_at_t); mfi.isValid(); ++mfi) {
382  const Box& bx = mfi.growntilebox();
383  Array4<int> const& src_arr = lmask_lev[lev][nt]->array(mfi);
384  Array4<Real> const& dst_arr = lmask_at_t.array(mfi);
385  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
386  {
387  dst_arr(i,j,k) = Real(src_arr(i,j,k));
388  });
389  }
390  VisMF::Write(lmask_at_t, MultiFabFileFullPrefix(lev, checkpointname, "Level_",
391  "LMASK_" + std::to_string(nt)));
392  }
393  }
394 
395  IntVect ngv = ng; ngv[2] = 0;
396 
397  // Write lat/lon if it exists
398  if (lat_m[lev] && lon_m[lev]) {
399  amrex::Print() << "Writing Lat/Lon variables at level " << lev << std::endl;
400  MultiFab lat(ba2d[lev],dmap[lev],1,ngv);
401  MultiFab lon(ba2d[lev],dmap[lev],1,ngv);
402  MultiFab::Copy(lat,*lat_m[lev],0,0,1,ngv);
403  MultiFab::Copy(lon,*lon_m[lev],0,0,1,ngv);
404  VisMF::Write(lat, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LAT"));
405  VisMF::Write(lon, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LON"));
406  }
407 
408 
409 #ifdef ERF_USE_NETCDF
410  // Write sinPhi and cosPhi if it exists
411  if (cosPhi_m[lev] && sinPhi_m[lev] && solverChoice.variable_coriolis) {
412  amrex::Print() << "Writing Coriolis factors at level " << lev << std::endl;
413  MultiFab sphi(ba2d[lev],dmap[lev],1,ngv);
414  MultiFab cphi(ba2d[lev],dmap[lev],1,ngv);
415  MultiFab::Copy(sphi,*sinPhi_m[lev],0,0,1,ngv);
416  MultiFab::Copy(cphi,*cosPhi_m[lev],0,0,1,ngv);
417  VisMF::Write(sphi, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "SinPhi"));
418  VisMF::Write(cphi, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "CosPhi"));
419  }
420 
421  if (solverChoice.use_real_bcs && solverChoice.init_type == InitType::WRFInput) {
422  if (lev == 0) {
423  amrex::Print() << "Writing C1H/C2H/MUB/PHB variables at level " << lev << std::endl;
424  MultiFab tmp1d(ba1d[0],dmap[0],1,0);
425 
426  MultiFab::Copy(tmp1d,*wrf_C1H,0,0,1,0);
427  VisMF::Write(tmp1d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "C1H"));
428 
429  MultiFab::Copy(tmp1d,*wrf_C2H,0,0,1,0);
430  VisMF::Write(tmp1d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "C2H"));
431 
432  MultiFab tmp2d(ba2d[0],dmap[0],1,wrf_MUB->nGrowVect());
433 
434  MultiFab::Copy(tmp2d,*wrf_MUB,0,0,1,wrf_MUB->nGrowVect());
435  VisMF::Write(tmp2d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MUB"));
436 
437  ng = IntVect(1,1,0);
438  MultiFab tmp3d(convert(grids[0],IntVect(0,0,1)),dmap[0],1,ng);
439  MultiFab::Copy(tmp3d,*wrf_PHB,0,0,1,ng);
440  VisMF::Write(tmp3d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "PHB"));
441  }
442  }
443 #endif
444  } // for lev
445 
446 #ifdef ERF_USE_PARTICLES
447  particleData.Checkpoint(checkpointname);
448 #endif
449 
450 #if 0
451 #ifdef ERF_USE_NETCDF
452  // Write bdy_data files
453  if ( ParallelDescriptor::IOProcessor() &&
454  ((solverChoice.init_type==InitType::WRFInput) || (solverChoice.init_type==InitType::Metgrid)) &&
456  {
457  // Vector dimensions
458  int num_time = bdy_data_xlo.size();
459  int num_var = bdy_data_xlo[0].size();
460 
461  // Open header file and write to it
462  std::ofstream bdy_h_file(MultiFabFileFullPrefix(0, checkpointname, "Level_", "bdy_H"));
463  bdy_h_file << std::setprecision(1) << std::fixed;
464  bdy_h_file << num_time << "\n";
465  bdy_h_file << num_var << "\n";
466  bdy_h_file << start_bdy_time << "\n";
467  bdy_h_file << bdy_time_interval << "\n";
468  bdy_h_file << real_width << "\n";
469  for (int ivar(0); ivar<num_var; ++ivar) {
470  bdy_h_file << bdy_data_xlo[0][ivar].box() << "\n";
471  bdy_h_file << bdy_data_xhi[0][ivar].box() << "\n";
472  bdy_h_file << bdy_data_ylo[0][ivar].box() << "\n";
473  bdy_h_file << bdy_data_yhi[0][ivar].box() << "\n";
474  }
475 
476  // Open data file and write to it
477  std::ofstream bdy_d_file(MultiFabFileFullPrefix(0, checkpointname, "Level_", "bdy_D"));
478  for (int itime(0); itime<num_time; ++itime) {
479  if (bdy_data_xlo[itime].size() > 0) {
480  for (int ivar(0); ivar<num_var; ++ivar) {
481  bdy_data_xlo[itime][ivar].writeOn(bdy_d_file,0,1);
482  bdy_data_xhi[itime][ivar].writeOn(bdy_d_file,0,1);
483  bdy_data_ylo[itime][ivar].writeOn(bdy_d_file,0,1);
484  bdy_data_yhi[itime][ivar].writeOn(bdy_d_file,0,1);
485  }
486  }
487  }
488  }
489 #endif
490 #endif
491 
492  if (verbose > 0)
493  {
494  auto dCheckTime = amrex::second() - dCheckTime0;
495  ParallelDescriptor::ReduceRealMax(dCheckTime,ParallelDescriptor::IOProcessorNumber());
496  amrex::Print() << "Checkpoint write time = " << dCheckTime << " seconds." << '\n';
497  }
498 }
int Get_LSM_Step(const int &lev) const
Definition: ERF_LandSurface.H:123
void Write_Lsm_Restart(const int &lev, const std::string &dir) const
Definition: ERF_LandSurface.H:132
bool variable_coriolis
Definition: ERF_DataStruct.H:1401
Here is the call graph for this function:

◆ WriteGenericPlotfileHeaderWithTerrain()

void ERF::WriteGenericPlotfileHeaderWithTerrain ( std::ostream &  HeaderFile,
int  nlevels,
const amrex::Vector< amrex::BoxArray > &  bArray,
const amrex::Vector< std::string > &  varnames,
const amrex::Vector< amrex::Geometry > &  my_geom,
amrex::Real  time,
const amrex::Vector< int > &  level_steps,
const amrex::Vector< amrex::IntVect > &  my_ref_ratio,
const std::string &  versionName,
const std::string &  levelPrefix,
const std::string &  mfPrefix 
) const
2042 {
2043  AMREX_ALWAYS_ASSERT(nlevels <= bArray.size());
2044  AMREX_ALWAYS_ASSERT(nlevels <= my_ref_ratio.size()+1);
2045  AMREX_ALWAYS_ASSERT(nlevels <= level_steps.size());
2046 
2047  HeaderFile.precision(17);
2048 
2049  // ---- this is the generic plot file type name
2050  HeaderFile << versionName << '\n';
2051 
2052  HeaderFile << varnames.size() << '\n';
2053 
2054  for (int ivar = 0; ivar < varnames.size(); ++ivar) {
2055  HeaderFile << varnames[ivar] << "\n";
2056  }
2057  HeaderFile << AMREX_SPACEDIM << '\n';
2058  HeaderFile << my_time << '\n';
2059  HeaderFile << finest_level << '\n';
2060  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
2061  HeaderFile << my_geom[0].ProbLo(i) << ' ';
2062  }
2063  HeaderFile << '\n';
2064  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
2065  HeaderFile << my_geom[0].ProbHi(i) << ' ';
2066  }
2067  HeaderFile << '\n';
2068  for (int i = 0; i < finest_level; ++i) {
2069  HeaderFile << my_ref_ratio[i][0] << ' ';
2070  }
2071  HeaderFile << '\n';
2072  for (int i = 0; i <= finest_level; ++i) {
2073  HeaderFile << my_geom[i].Domain() << ' ';
2074  }
2075  HeaderFile << '\n';
2076  for (int i = 0; i <= finest_level; ++i) {
2077  HeaderFile << level_steps[i] << ' ';
2078  }
2079  HeaderFile << '\n';
2080  for (int i = 0; i <= finest_level; ++i) {
2081  for (int k = 0; k < AMREX_SPACEDIM; ++k) {
2082  HeaderFile << my_geom[i].CellSize()[k] << ' ';
2083  }
2084  HeaderFile << '\n';
2085  }
2086  HeaderFile << (int) my_geom[0].Coord() << '\n';
2087  HeaderFile << "0\n";
2088 
2089  for (int level = 0; level <= finest_level; ++level) {
2090  HeaderFile << level << ' ' << bArray[level].size() << ' ' << my_time << '\n';
2091  HeaderFile << level_steps[level] << '\n';
2092 
2093  const IntVect& domain_lo = my_geom[level].Domain().smallEnd();
2094  for (int i = 0; i < bArray[level].size(); ++i)
2095  {
2096  // Need to shift because the RealBox ctor we call takes the
2097  // physical location of index (0,0,0). This does not affect
2098  // the usual cases where the domain index starts with zero
2099  const Box& b = shift(bArray[level][i], -domain_lo);
2100  RealBox loc = RealBox(b, my_geom[level].CellSize(), my_geom[level].ProbLo());
2101  for (int n = 0; n < AMREX_SPACEDIM; ++n) {
2102  HeaderFile << loc.lo(n) << ' ' << loc.hi(n) << '\n';
2103  }
2104  }
2105 
2106  HeaderFile << MultiFabHeaderPath(level, levelPrefix, mfPrefix) << '\n';
2107  }
2108  HeaderFile << "1" << "\n";
2109  HeaderFile << "3" << "\n";
2110  HeaderFile << "amrexvec_nu_x" << "\n";
2111  HeaderFile << "amrexvec_nu_y" << "\n";
2112  HeaderFile << "amrexvec_nu_z" << "\n";
2113  std::string mf_nodal_prefix = "Nu_nd";
2114  for (int level = 0; level <= finest_level; ++level) {
2115  HeaderFile << MultiFabHeaderPath(level, levelPrefix, mf_nodal_prefix) << '\n';
2116  }
2117 }
Coord
Definition: ERF_DataStruct.H:92
Here is the call graph for this function:

◆ writeJobInfo()

void ERF::writeJobInfo ( const std::string &  dir) const
10 {
11  // job_info file with details about the run
12  std::ofstream jobInfoFile;
13  std::string FullPathJobInfoFile = dir;
14  FullPathJobInfoFile += "/job_info";
15  jobInfoFile.open(FullPathJobInfoFile.c_str(), std::ios::out);
16 
17  std::string PrettyLine = "==================================================="
18  "============================\n";
19  std::string OtherLine = "----------------------------------------------------"
20  "----------------------------\n";
21  std::string SkipSpace = " ";
22 
23  // job information
24  jobInfoFile << PrettyLine;
25  jobInfoFile << " ERF Job Information\n";
26  jobInfoFile << PrettyLine;
27 
28  jobInfoFile << "inputs file: " << inputs_name << "\n\n";
29 
30  jobInfoFile << "number of MPI processes: "
31  << ParallelDescriptor::NProcs() << "\n";
32 #ifdef _OPENMP
33  jobInfoFile << "number of threads: " << omp_get_max_threads() << "\n";
34 #endif
35 
36  jobInfoFile << "\n";
37  jobInfoFile << "CPU time used since start of simulation (CPU-hours): "
38  << getCPUTime() / Real(3600.0);
39 
40  jobInfoFile << "\n\n";
41 
42  if (use_datetime) {
43  const std::string dt_format = "%Y-%m-%d %H:%M:%S"; // ISO 8601 standard
44  jobInfoFile << "Simulation time: " << getTimestamp(start_time+t_new[0], dt_format) << "\n";
45  jobInfoFile << "\n\n";
46  }
47 
48  // plotfile information
49  jobInfoFile << PrettyLine;
50  jobInfoFile << " Plotfile Information\n";
51  jobInfoFile << PrettyLine;
52 
53  time_t now = time(nullptr);
54 
55  // Convert now to tm struct for local timezone
56  tm* localtm = localtime(&now);
57  jobInfoFile << "output data / time: " << asctime(localtm);
58 
59  std::string currentDir = FileSystem::CurrentPath();
60  jobInfoFile << "output dir: " << currentDir << "\n";
61 
62  jobInfoFile << "\n\n";
63 
64  // build information
65  jobInfoFile << PrettyLine;
66  jobInfoFile << " Build Information\n";
67  jobInfoFile << PrettyLine;
68 
69  jobInfoFile << "build date: " << buildInfoGetBuildDate() << "\n";
70  jobInfoFile << "build machine: " << buildInfoGetBuildMachine() << "\n";
71  jobInfoFile << "build dir: " << buildInfoGetBuildDir() << "\n";
72  jobInfoFile << "AMReX dir: " << buildInfoGetAMReXDir() << "\n";
73 
74  jobInfoFile << "\n";
75 
76  jobInfoFile << "COMP: " << buildInfoGetComp() << "\n";
77  jobInfoFile << "COMP version: " << buildInfoGetCompVersion() << "\n";
78 
79  jobInfoFile << "\n";
80 
81  for (int n = 1; n <= buildInfoGetNumModules(); n++) {
82  jobInfoFile << buildInfoGetModuleName(n) << ": "
83  << buildInfoGetModuleVal(n) << "\n";
84  }
85 
86  jobInfoFile << "\n";
87 
88  const char* githash1 = buildInfoGetGitHash(1);
89  const char* githash2 = buildInfoGetGitHash(2);
90  if (strlen(githash1) > 0) {
91  jobInfoFile << "ERF git hash: " << githash1 << "\n";
92  }
93  if (strlen(githash2) > 0) {
94  jobInfoFile << "AMReX git hash: " << githash2 << "\n";
95  }
96 
97  const char* buildgithash = buildInfoGetBuildGitHash();
98  const char* buildgitname = buildInfoGetBuildGitName();
99  if (strlen(buildgithash) > 0) {
100  jobInfoFile << buildgitname << " git hash: " << buildgithash << "\n";
101  }
102 
103  jobInfoFile << "\n\n";
104 
105  // grid information
106  jobInfoFile << PrettyLine;
107  jobInfoFile << " Grid Information\n";
108  jobInfoFile << PrettyLine;
109 
110  int f_lev = finest_level;
111 
112  for (int i = 0; i <= f_lev; i++) {
113  jobInfoFile << " level: " << i << "\n";
114  jobInfoFile << " number of boxes = " << grids[i].size() << "\n";
115  jobInfoFile << " maximum zones = ";
116  for (int n = 0; n < AMREX_SPACEDIM; n++) {
117  jobInfoFile << geom[i].Domain().length(n) << " ";
118  }
119  jobInfoFile << "\n\n";
120  }
121 
122  jobInfoFile << " Boundary conditions\n";
123 
124  jobInfoFile << " -x: " << domain_bc_type[0] << "\n";
125  jobInfoFile << " +x: " << domain_bc_type[3] << "\n";
126  jobInfoFile << " -y: " << domain_bc_type[1] << "\n";
127  jobInfoFile << " +y: " << domain_bc_type[4] << "\n";
128  jobInfoFile << " -z: " << domain_bc_type[2] << "\n";
129  jobInfoFile << " +z: " << domain_bc_type[5] << "\n";
130 
131  jobInfoFile << "\n\n";
132 
133  // runtime parameters
134  jobInfoFile << PrettyLine;
135  jobInfoFile << " Inputs File Parameters\n";
136  jobInfoFile << PrettyLine;
137 
138  ParmParse::dumpTable(jobInfoFile, true);
139  jobInfoFile.close();
140 }
std::string inputs_name
Definition: main.cpp:15
static amrex::Real getCPUTime()
Definition: ERF.H:1610
Here is the call graph for this function:

◆ WriteLinePlot()

void ERF::WriteLinePlot ( const std::string &  filename,
amrex::Vector< std::array< amrex::Real, 2 >> &  points_xy 
)
595 {
596  std::ofstream ofs(filename);
597  if (!ofs.is_open()) {
598  amrex::Print() << "Error: Could not open file " << filename << " for writing.\n";
599  return;
600  }
601 
602  ofs << std::setprecision(10) << std::scientific;
603  ofs << "# x y\n";
604 
605  for (const auto& p : points_xy) {
606  ofs << p[0] << " " << p[1] << "\n";
607  }
608 
609  ofs.close();
610 
611  amrex::Print() << "Line plot data written to " << filename << "\n";
612 }

◆ WriteMultiLevelPlotfileWithTerrain()

void ERF::WriteMultiLevelPlotfileWithTerrain ( const std::string &  plotfilename,
int  nlevels,
const amrex::Vector< const amrex::MultiFab * > &  mf,
const amrex::Vector< const amrex::MultiFab * > &  mf_nd,
const amrex::Vector< std::string > &  varnames,
const amrex::Vector< amrex::Geometry > &  my_geom,
amrex::Real  time,
const amrex::Vector< int > &  level_steps,
const amrex::Vector< amrex::IntVect > &  my_ref_ratio,
const std::string &  versionName = "HyperCLaw-V1.1",
const std::string &  levelPrefix = "Level_",
const std::string &  mfPrefix = "Cell",
const amrex::Vector< std::string > &  extra_dirs = amrex::Vector<std::string>() 
) const
1956 {
1957  BL_PROFILE("WriteMultiLevelPlotfileWithTerrain()");
1958 
1959  AMREX_ALWAYS_ASSERT(nlevels <= mf.size());
1960  AMREX_ALWAYS_ASSERT(nlevels <= rr.size()+1);
1961  AMREX_ALWAYS_ASSERT(nlevels <= level_steps.size());
1962  AMREX_ALWAYS_ASSERT(mf[0]->nComp() == varnames.size());
1963 
1964  bool callBarrier(false);
1965  PreBuildDirectorHierarchy(plotfilename, levelPrefix, nlevels, callBarrier);
1966  if (!extra_dirs.empty()) {
1967  for (const auto& d : extra_dirs) {
1968  const std::string ed = plotfilename+"/"+d;
1969  PreBuildDirectorHierarchy(ed, levelPrefix, nlevels, callBarrier);
1970  }
1971  }
1972  ParallelDescriptor::Barrier();
1973 
1974  if (ParallelDescriptor::MyProc() == ParallelDescriptor::NProcs()-1) {
1975  Vector<BoxArray> boxArrays(nlevels);
1976  for(int level(0); level < boxArrays.size(); ++level) {
1977  boxArrays[level] = mf[level]->boxArray();
1978  }
1979 
1980  auto f = [=,this]() {
1981  VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
1982  std::string HeaderFileName(plotfilename + "/Header");
1983  std::ofstream HeaderFile;
1984  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
1985  HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
1986  std::ofstream::trunc |
1987  std::ofstream::binary);
1988  if( ! HeaderFile.good()) FileOpenFailed(HeaderFileName);
1989  WriteGenericPlotfileHeaderWithTerrain(HeaderFile, nlevels, boxArrays, varnames,
1990  my_geom, time, level_steps, rr, versionName,
1991  levelPrefix, mfPrefix);
1992  };
1993 
1994  if (AsyncOut::UseAsyncOut()) {
1995  AsyncOut::Submit(std::move(f));
1996  } else {
1997  f();
1998  }
1999  }
2000 
2001  std::string mf_nodal_prefix = "Nu_nd";
2002  for (int level = 0; level <= finest_level; ++level)
2003  {
2004  if (AsyncOut::UseAsyncOut()) {
2005  VisMF::AsyncWrite(*mf[level],
2006  MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mfPrefix),
2007  true);
2008  VisMF::AsyncWrite(*mf_nd[level],
2009  MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mf_nodal_prefix),
2010  true);
2011  } else {
2012  const MultiFab* data;
2013  std::unique_ptr<MultiFab> mf_tmp;
2014  if (mf[level]->nGrowVect() != 0) {
2015  mf_tmp = std::make_unique<MultiFab>(mf[level]->boxArray(),
2016  mf[level]->DistributionMap(),
2017  mf[level]->nComp(), 0, MFInfo(),
2018  mf[level]->Factory());
2019  MultiFab::Copy(*mf_tmp, *mf[level], 0, 0, mf[level]->nComp(), 0);
2020  data = mf_tmp.get();
2021  } else {
2022  data = mf[level];
2023  }
2024  VisMF::Write(*data , MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mfPrefix));
2025  VisMF::Write(*mf_nd[level], MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mf_nodal_prefix));
2026  }
2027  }
2028 }
void WriteGenericPlotfileHeaderWithTerrain(std::ostream &HeaderFile, int nlevels, const amrex::Vector< amrex::BoxArray > &bArray, const amrex::Vector< std::string > &varnames, const amrex::Vector< amrex::Geometry > &my_geom, amrex::Real time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &my_ref_ratio, const std::string &versionName, const std::string &levelPrefix, const std::string &mfPrefix) const
Definition: ERF_Plotfile.cpp:2031
Here is the call graph for this function:

◆ WriteMyEBSurface()

void ERF::WriteMyEBSurface ( )

◆ writeNow()

bool ERF::writeNow ( double  cur_time,
const int  nstep,
const int  plot_int,
const amrex::Real  plot_per,
const amrex::Real  dt_0,
amrex::Real last_file_time 
)
2771 {
2772  bool write_now = false;
2773 
2774  if ( plot_int > 0) {
2775 
2776  write_now = (nstep % plot_int == 0);
2777 
2778  } else if (plot_per > zero) {
2779 
2780  amrex::Print() << "CUR NEXT PER " << cur_time << " " << next_file_time << " " << plot_per << std::endl;
2781 
2782  // Only write now if nstep newly matches the number of elapsed periods
2783  write_now = (cur_time > (next_file_time - Real(0.1)*dt_0));
2784  }
2785 
2786  return write_now;
2787 }

◆ WriteSubvolume()

void ERF::WriteSubvolume ( int  isub,
amrex::Vector< std::string >  subvol_var_names 
)
146 {
147  ParmParse pp("erf.subvol");
148 
149  Vector<Real> origin;
150  Vector< int> ncell;
151  Vector<Real> delta;
152 
153  // **************************************************************
154  // Read in the origin, number of cells in each dir, and resolution
155  // **************************************************************
156 
157  int lev_for_sub = 0;
158  int offset = isub * AMREX_SPACEDIM;
159 
160  pp.getarr("origin",origin,offset,AMREX_SPACEDIM);
161  pp.getarr("nxnynz", ncell,offset,AMREX_SPACEDIM);
162  pp.getarr("dxdydz", delta,offset,AMREX_SPACEDIM);
163 
164  bool found = false;
165  for (int i = 0; i <= finest_level; i++) {
166  if (!found) {
167  if (almostEqual(delta[offset+0],geom[i].CellSize(0)) &&
168  almostEqual(delta[offset+1],geom[i].CellSize(1)) &&
169  almostEqual(delta[offset+2],geom[i].CellSize(2)) ) {
170 
171  amrex::Print() << "WriteSubvolume:Resolution specified matches that of level " << i << std::endl;
172  found = true;
173  lev_for_sub = i;
174  }
175  }
176  }
177 
178  if (!found) {
179  amrex::Abort("Resolution specified for subvol does not match the resolution of any of the levels.");
180  }
181 
182 
183  // **************************************************************
184  // Now that we know which level we're at, we can figure out which (i,j,k) the origin corresponds to
185  // Note we use Real(1.0001) as a fudge factor since the division of two reals --> integer will do a floor
186  // **************************************************************
187  int i0 = static_cast<int>((origin[offset+0] - geom[lev_for_sub].ProbLo(0)) * Real(1.0001) / delta[offset+0]);
188  int j0 = static_cast<int>((origin[offset+1] - geom[lev_for_sub].ProbLo(1)) * Real(1.0001) / delta[offset+1]);
189  int k0 = static_cast<int>((origin[offset+2] - geom[lev_for_sub].ProbLo(2)) * Real(1.0001) / delta[offset+2]);
190 
191  found = false;
192  if (almostEqual(geom[lev_for_sub].ProbLo(0)+i0*delta[offset+0],origin[offset+0]) &&
193  almostEqual(geom[lev_for_sub].ProbLo(1)+j0*delta[offset+1],origin[offset+1]) &&
194  almostEqual(geom[lev_for_sub].ProbLo(2)+k0*delta[offset+2],origin[offset+2]) )
195  {
196  amrex::Print() << "WriteSubvolume:Specified origin is the lower left corner of cell " << IntVect(i0,j0,k0) << std::endl;
197  found = true;
198  }
199 
200  if (!found) {
201  amrex::Abort("Origin specified does not correspond to a node at this level.");
202  }
203 
204  Box domain(geom[lev_for_sub].Domain());
205 
206  Box bx(IntVect(i0,j0,k0),IntVect(i0+ncell[offset+0]-1,j0+ncell[offset+1]-1,k0+ncell[offset+2]-1));
207  amrex::Print() << "WriteSubvolume:Box requested is " << bx << std::endl;
208 
209  if (!domain.contains(bx))
210  {
211  amrex::Abort("WriteSubvolume:Box requested is larger than the existing domain");
212  }
213 
214  Vector<int> cs(AMREX_SPACEDIM);
215  int count = pp.countval("chunk_size");
216  if (count > 0) {
217  pp.queryarr("chunk_size",cs,0,AMREX_SPACEDIM);
218  } else {
219  cs[0] = max_grid_size[0][0];
220  cs[1] = max_grid_size[0][1];
221  cs[2] = max_grid_size[0][2];
222  }
223  IntVect chunk_size(cs[0],cs[1],cs[2]);
224 
225  BoxArray ba(bx);
226  ba.maxSize(chunk_size);
227 
228  amrex::Print() << "WriteSubvolume:BoxArray is " << ba << std::endl;
229 
230  Vector<std::string> varnames;
231  varnames.insert(varnames.end(), subvol_var_names.begin(), subvol_var_names.end());
232 
233  int ncomp_mf = subvol_var_names.size();
234 
235  DistributionMapping dm(ba);
236 
237  MultiFab mf(ba, dm, ncomp_mf, 0);
238 
239  int mf_comp = 0;
240 
241  // *****************************************************************************************
242 
243  // First, copy any of the conserved state variables into the output plotfile
244  for (int i = 0; i < cons_names.size(); ++i) {
245  if (containerHasElement(subvol_var_names, cons_names[i])) {
246  mf.ParallelCopy(vars_new[lev_for_sub][Vars::cons],i,mf_comp,1,1,0);
247  mf_comp++;
248  }
249  }
250 
251  // *****************************************************************************************
252 
253  if (containerHasElement(subvol_var_names, "x_velocity") ||
254  containerHasElement(subvol_var_names, "y_velocity") ||
255  containerHasElement(subvol_var_names, "z_velocity"))
256  {
257  MultiFab mf_cc_vel(grids[lev_for_sub], dmap[lev_for_sub], AMREX_SPACEDIM, 0);
258  average_face_to_cellcenter(mf_cc_vel,0,
259  Array<const MultiFab*,3>{&vars_new[lev_for_sub][Vars::xvel],
260  &vars_new[lev_for_sub][Vars::yvel],
261  &vars_new[lev_for_sub][Vars::zvel]});
262  if (containerHasElement(subvol_var_names, "x_velocity")) {
263  mf.ParallelCopy(mf_cc_vel,0,mf_comp,1,0,0);
264  mf_comp++;
265  }
266  if (containerHasElement(subvol_var_names, "y_velocity")) {
267  mf.ParallelCopy(mf_cc_vel,1,mf_comp,1,0,0);
268  mf_comp++;
269  }
270  if (containerHasElement(subvol_var_names, "z_velocity")) {
271  mf.ParallelCopy(mf_cc_vel,2,mf_comp,1,0,0);
272  mf_comp++;
273  }
274  }
275 
276  // *****************************************************************************************
277 
278  // Finally, check for any derived quantities and compute them, inserting
279  // them into our output multifab
280  auto calculate_derived = [&](const std::string& der_name,
281  MultiFab& src_mf,
282  decltype(derived::erf_dernull)& der_function)
283  {
284  if (containerHasElement(subvol_var_names, der_name)) {
285  MultiFab dmf(src_mf.boxArray(), src_mf.DistributionMap(), 1, 0);
286 #ifdef _OPENMP
287 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
288 #endif
289  for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
290  {
291  const Box& tbx = mfi.tilebox();
292  auto& dfab = dmf[mfi];
293  auto& sfab = src_mf[mfi];
294  auto& zfab = (*z_phys_cc[lev_for_sub])[mfi];
295  der_function(tbx, dfab, 0, 1, sfab, zfab, Geom(lev_for_sub), t_new[0], nullptr, lev_for_sub);
296  }
297  mf.ParallelCopy(dmf,0,mf_comp,1,0,0);
298  mf_comp++;
299  }
300  };
301 
302  // *****************************************************************************************
303  // NOTE: All derived variables computed below **MUST MATCH THE ORDER** of "derived_names"
304  // defined in ERF.H
305  // *****************************************************************************************
306 
307  if (solverChoice.moisture_type != MoistureType::None) {
308  calculate_derived("temp", vars_new[lev_for_sub][Vars::cons], derived::erf_dermoisttemp);
309  } else {
310  calculate_derived("temp", vars_new[lev_for_sub][Vars::cons], derived::erf_dertemp);
311  }
312  calculate_derived("theta", vars_new[lev_for_sub][Vars::cons], derived::erf_dertheta);
313  calculate_derived("KE", vars_new[lev_for_sub][Vars::cons], derived::erf_derKE);
314  calculate_derived("scalar", vars_new[lev_for_sub][Vars::cons], derived::erf_derscalar);
315  calculate_derived("soundspeed", vars_new[lev_for_sub][Vars::cons], derived::erf_dersoundspeed);
316  if (solverChoice.moisture_type != MoistureType::None) {
317  calculate_derived("precipitable", vars_new[lev_for_sub][Vars::cons], derived::erf_derprecipitable);
318  calculate_derived("mucape", vars_new[lev_for_sub][Vars::cons], derived::erf_dermucape);
319  }
320 
321  // *****************************************************************************************
322 
323  Real time = t_new[lev_for_sub];
324 
325  std::string sf = subvol_file + "_" + std::to_string(isub);
326  std::string subvol_filename;
327 
329  const std::string dt_format = "%Y-%m-%d_%H:%M:%S"; // ISO 8601 standard
330  subvol_filename = sf + getTimestamp(start_time+time, dt_format);
331  } else {
332  subvol_filename = Concatenate(sf + "_", istep[0], file_name_digits);
333  }
334 
335  amrex::Print() <<"Writing subvolume into " << subvol_filename << std::endl;
336  WriteSingleLevelPlotfile(subvol_filename,mf,varnames,geom[lev_for_sub],time,istep[0]);
337 
338 }
real(c_double), private cs
Definition: ERF_module_mp_morr_two_moment.F90:203
Here is the call graph for this function:

◆ WriteVTKPolyline()

void ERF::WriteVTKPolyline ( const std::string &  filename,
amrex::Vector< std::array< amrex::Real, 2 >> &  points_xy 
)
96 {
97  std::ofstream vtkfile(filename);
98  if (!vtkfile.is_open()) {
99  std::cerr << "Error: Cannot open file " << filename << std::endl;
100  return;
101  }
102 
103  int num_points = points_xy.size();
104  if (num_points == 0) {
105  vtkfile << "# vtk DataFile Version three\n";
106  vtkfile << "Storm Track\n";
107  vtkfile << "ASCII\n";
108  vtkfile << "DATASET POLYDATA\n";
109  vtkfile << "POINTS " << num_points << " float\n";
110  vtkfile.close();
111  return;
112  }
113  if (num_points < 2) {
114  points_xy.push_back(points_xy[0]);
115  }
116  num_points = points_xy.size();
117 
118  vtkfile << "# vtk DataFile Version three\n";
119  vtkfile << "Storm Track\n";
120  vtkfile << "ASCII\n";
121  vtkfile << "DATASET POLYDATA\n";
122 
123  // Write points (Z=0 assumed)
124  vtkfile << "POINTS " << num_points << " float\n";
125  for (const auto& pt : points_xy) {
126  vtkfile << pt[0] << " " << pt[1] << 10000.0 << std::endl;
127  }
128 
129  // Write polyline connectivity
130  vtkfile << "LINES 1 " << num_points + 1 << "\n";
131  vtkfile << num_points << " ";
132  for (int i = 0; i < num_points; ++i) {
133  vtkfile << i << " ";
134  }
135  vtkfile << "\n";
136 
137  vtkfile.close();
138 }

Member Data Documentation

◆ advflux_reg

amrex::Vector<amrex::YAFluxRegister*> ERF::advflux_reg
private

Referenced by ERF_shared(), and getAdvFluxReg().

◆ avg_xmom

amrex::Vector<amrex::MultiFab> ERF::avg_xmom
private

Referenced by ERF_shared().

◆ avg_ymom

amrex::Vector<amrex::MultiFab> ERF::avg_ymom
private

Referenced by ERF_shared().

◆ avg_zmom

amrex::Vector<amrex::MultiFab> ERF::avg_zmom
private

Referenced by ERF_shared().

◆ ax

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::ax
private

Referenced by ERF_shared().

◆ ax_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::ax_src
private

Referenced by ERF_shared().

◆ ay

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::ay
private

Referenced by ERF_shared().

◆ ay_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::ay_src
private

Referenced by ERF_shared().

◆ az

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::az
private

Referenced by ERF_shared().

◆ az_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::az_src
private

Referenced by ERF_shared().

◆ ba1d

amrex::Vector<amrex::BoxArray> ERF::ba1d
private

Referenced by ERF_shared().

◆ ba2d

amrex::Vector<amrex::BoxArray> ERF::ba2d
private

◆ base_state

amrex::Vector<amrex::MultiFab> ERF::base_state
private

Referenced by ERF_shared().

◆ base_state_new

amrex::Vector<amrex::MultiFab> ERF::base_state_new
private

Referenced by ERF_shared().

◆ bckgnd_state

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::bckgnd_state

◆ bndry_output_planes_interval

int ERF::bndry_output_planes_interval = -1
staticprivate

◆ bndry_output_planes_per

Real ERF::bndry_output_planes_per = -one
staticprivate

◆ bndry_output_planes_start_time

Real ERF::bndry_output_planes_start_time = zero
staticprivate

◆ boxes_at_level

amrex::Vector<amrex::Vector<amrex::Box> > ERF::boxes_at_level
private

◆ cf_set_width

int ERF::cf_set_width {0}
private

◆ cf_width

int ERF::cf_width {0}
private

◆ cfl

Real ERF::cfl = Real(0.8)
staticprivate

◆ change_max

Real ERF::change_max = Real(1.1)
staticprivate

◆ check_file

std::string ERF::check_file {"chk"}
private

◆ check_for_nans

int ERF::check_for_nans = 0
staticprivate

◆ column_file_name

std::string ERF::column_file_name = "column_data.nc"
staticprivate

◆ column_interval

int ERF::column_interval = -1
staticprivate

◆ column_loc_x

Real ERF::column_loc_x = zero
staticprivate

◆ column_loc_y

Real ERF::column_loc_y = zero
staticprivate

◆ column_per

Real ERF::column_per = -one
staticprivate

◆ cons_names

const amrex::Vector<std::string> ERF::cons_names
private
Initial value:
{"density", "rhotheta", "rhoKE", "rhoadv_0",
"rhoQ1", "rhoQ2", "rhoQ3",
"rhoQ4", "rhoQ5", "rhoQ6",
"rhoQ7", "rhoQ8", "rhoQ9",
"rhoQ10", "rhoQ11"}

◆ cosPhi_m

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::cosPhi_m
private

Referenced by ERF_shared().

◆ d_havg_density

amrex::Gpu::DeviceVector<amrex::Real> ERF::d_havg_density
private

◆ d_havg_pressure

amrex::Gpu::DeviceVector<amrex::Real> ERF::d_havg_pressure
private

◆ d_havg_qc

amrex::Gpu::DeviceVector<amrex::Real> ERF::d_havg_qc
private

◆ d_havg_qv

amrex::Gpu::DeviceVector<amrex::Real> ERF::d_havg_qv
private

◆ d_havg_temperature

amrex::Gpu::DeviceVector<amrex::Real> ERF::d_havg_temperature
private

◆ d_rayleigh_ptrs

amrex::Vector<amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > > ERF::d_rayleigh_ptrs
private

Referenced by ERF_shared().

◆ d_sinesq_ptrs

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::d_sinesq_ptrs
private

Referenced by ERF_shared().

◆ d_sinesq_stag_ptrs

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::d_sinesq_stag_ptrs
private

Referenced by ERF_shared().

◆ d_sponge_ptrs

amrex::Vector<amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > > ERF::d_sponge_ptrs
private

◆ d_u_geos

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::d_u_geos
private

◆ d_v_geos

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::d_v_geos
private

◆ d_w_subsid

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::d_w_subsid
private

◆ datalog

amrex::Vector<std::unique_ptr<std::fstream> > ERF::datalog
private

◆ datalogname

amrex::Vector<std::string> ERF::datalogname
private

Referenced by DataLogName().

◆ datetime_format

const std::string ERF::datetime_format = "%Y-%m-%d %H:%M:%S"
private

◆ datprecision

const int ERF::datprecision = 6
private

◆ datwidth

const int ERF::datwidth = 14
private

◆ der_datalog

amrex::Vector<std::unique_ptr<std::fstream> > ERF::der_datalog
private

◆ der_datalogname

amrex::Vector<std::string> ERF::der_datalogname
private

Referenced by DerDataLogName().

◆ derived_names

const amrex::Vector<std::string> ERF::derived_names
private

◆ derived_subvol_names

const amrex::Vector<std::string> ERF::derived_subvol_names {"soundspeed", "temp", "theta", "KE", "scalar"}
private

◆ destag_profiles

bool ERF::destag_profiles = true
private

◆ detJ_cc

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::detJ_cc
private

Referenced by ERF_shared().

◆ detJ_cc_new

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::detJ_cc_new
private

Referenced by ERF_shared().

◆ detJ_cc_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::detJ_cc_src
private

Referenced by ERF_shared().

◆ domain_bc_type

amrex::Array<std::string,2*AMREX_SPACEDIM> ERF::domain_bc_type
private

◆ domain_bcs_type

amrex::Vector<amrex::BCRec> ERF::domain_bcs_type
private

◆ domain_bcs_type_d

amrex::Gpu::DeviceVector<amrex::BCRec> ERF::domain_bcs_type_d
private

◆ dt

amrex::Vector<amrex::Real> ERF::dt
private

Referenced by ERF_shared(), and EvolveOneStep().

◆ dt_max

Real ERF::dt_max = Real(1.0e9)
staticprivate

◆ dt_max_initial

Real ERF::dt_max_initial = bogus_large_value
staticprivate

Referenced by ERF_shared().

◆ dt_mri_ratio

amrex::Vector<long> ERF::dt_mri_ratio
private

Referenced by ERF_shared().

◆ dz_min

amrex::Vector<amrex::Real> ERF::dz_min
private

◆ eb

amrex::Vector<std::unique_ptr<eb_> > ERF::eb
private

Referenced by EBFactory(), ERF_shared(), and get_eb().

◆ eddyDiffs_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::eddyDiffs_lev
private

Referenced by ERF_shared().

◆ erfbdy_file

std::string ERF::erfbdy_file {"erfbdy"}
private

◆ file_name_digits

int ERF::file_name_digits = 5
private

◆ fine_mask

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::fine_mask
private

Referenced by ERF_shared().

◆ finished_wave

bool ERF::finished_wave = false
private

◆ fixed_dt

amrex::Vector<amrex::Real> ERF::fixed_dt
private

◆ fixed_fast_dt

amrex::Vector<amrex::Real> ERF::fixed_fast_dt
private

◆ fixed_mri_dt_ratio

int ERF::fixed_mri_dt_ratio = 0
staticprivate

◆ forecast_state_1

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::forecast_state_1

Referenced by ERF_shared().

◆ forecast_state_2

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::forecast_state_2

Referenced by ERF_shared().

◆ forecast_state_interp

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::forecast_state_interp

Referenced by ERF_shared().

◆ FPr_c

amrex::Vector<ERFFillPatcher> ERF::FPr_c
private

◆ FPr_u

amrex::Vector<ERFFillPatcher> ERF::FPr_u
private

◆ FPr_v

amrex::Vector<ERFFillPatcher> ERF::FPr_v
private

◆ FPr_w

amrex::Vector<ERFFillPatcher> ERF::FPr_w
private

◆ gradp

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::gradp
private

Referenced by ERF_shared().

◆ h_havg_density

amrex::Vector<amrex::Real> ERF::h_havg_density
private

◆ h_havg_pressure

amrex::Vector<amrex::Real> ERF::h_havg_pressure
private

◆ h_havg_qc

amrex::Vector<amrex::Real> ERF::h_havg_qc
private

◆ h_havg_qv

amrex::Vector<amrex::Real> ERF::h_havg_qv
private

◆ h_havg_temperature

amrex::Vector<amrex::Real> ERF::h_havg_temperature
private

◆ h_rayleigh_ptrs

amrex::Vector<amrex::Vector<amrex::Vector<amrex::Real> > > ERF::h_rayleigh_ptrs
private

Referenced by ERF_shared().

◆ h_sinesq_ptrs

amrex::Vector<amrex::Vector<amrex::Real> > ERF::h_sinesq_ptrs
private

Referenced by ERF_shared().

◆ h_sinesq_stag_ptrs

amrex::Vector<amrex::Vector<amrex::Real> > ERF::h_sinesq_stag_ptrs
private

Referenced by ERF_shared().

◆ h_sponge_ptrs

amrex::Vector<amrex::Vector<amrex::Vector<amrex::Real> > > ERF::h_sponge_ptrs
private

◆ h_u_geos

amrex::Vector< amrex::Vector<amrex::Real> > ERF::h_u_geos
private

◆ h_v_geos

amrex::Vector< amrex::Vector<amrex::Real> > ERF::h_v_geos
private

◆ h_w_subsid

amrex::Vector< amrex::Vector<amrex::Real> > ERF::h_w_subsid
private

◆ have_read_nc_init_file

Vector< Vector< int > > ERF::have_read_nc_init_file = {{0}}
staticprivate

◆ hfx3_EB

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::hfx3_EB
private

Referenced by ERF_shared().

◆ hurricane_eye_track_latlon

amrex::Vector<std::array<amrex::Real, 2> > ERF::hurricane_eye_track_latlon

◆ hurricane_eye_track_xy

amrex::Vector<std::array<amrex::Real, 2> > ERF::hurricane_eye_track_xy

◆ hurricane_maxvel_vs_time

amrex::Vector<std::array<amrex::Real, 2> > ERF::hurricane_maxvel_vs_time

◆ hurricane_minpressure_vs_time

amrex::Vector<std::array<amrex::Real, 2> > ERF::hurricane_minpressure_vs_time

◆ hurricane_track_xy

amrex::Vector<std::array<amrex::Real, 2> > ERF::hurricane_track_xy

◆ hurricane_tracker_circle

amrex::Vector<std::array<amrex::Real, 2> > ERF::hurricane_tracker_circle

◆ Hwave

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Hwave
private

Referenced by ERF_shared().

◆ Hwave_onegrid

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Hwave_onegrid
private

Referenced by ERF_shared().

◆ init_shrink

Real ERF::init_shrink = one
staticprivate

◆ input_bndry_planes

int ERF::input_bndry_planes = 0
staticprivate

◆ input_sounding_data

InputSoundingData ERF::input_sounding_data
private

◆ input_sponge_data

InputSpongeData ERF::input_sponge_data
private

◆ interpolation_type

StateInterpType ERF::interpolation_type
staticprivate

◆ istep

amrex::Vector<int> ERF::istep
private

Referenced by EvolveOneStep().

◆ lagged_delta_rt

amrex::Vector<amrex::MultiFab> ERF::lagged_delta_rt
private

Referenced by ERF_shared().

◆ land_type_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::iMultiFab> > > ERF::land_type_lev
private

Referenced by ERF_shared().

◆ last_check_file_step

int ERF::last_check_file_step = -1
staticprivate

◆ last_check_file_time

Real ERF::last_check_file_time = zero
staticprivate

◆ last_plot2d_file_step_1

int ERF::last_plot2d_file_step_1 = -1
staticprivate

◆ last_plot2d_file_step_2

int ERF::last_plot2d_file_step_2 = -1
staticprivate

◆ last_plot2d_file_time_1

Real ERF::last_plot2d_file_time_1 = zero
staticprivate

◆ last_plot2d_file_time_2

Real ERF::last_plot2d_file_time_2 = zero
staticprivate

◆ last_plot3d_file_step_1

int ERF::last_plot3d_file_step_1 = -1
staticprivate

◆ last_plot3d_file_step_2

int ERF::last_plot3d_file_step_2 = -1
staticprivate

◆ last_plot3d_file_time_1

Real ERF::last_plot3d_file_time_1 = zero
staticprivate

◆ last_plot3d_file_time_2

Real ERF::last_plot3d_file_time_2 = zero
staticprivate

◆ last_subvol_step

amrex::Vector<int> ERF::last_subvol_step
private

◆ last_subvol_time

amrex::Vector<amrex::Real> ERF::last_subvol_time
private

◆ lat_m

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::lat_m
private

Referenced by ERF_shared().

◆ line_sampler

std::unique_ptr<LineSampler> ERF::line_sampler = nullptr
private

◆ line_sampling_interval

int ERF::line_sampling_interval = -1
private

◆ line_sampling_per

amrex::Real ERF::line_sampling_per = -one
private

◆ lmask_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::iMultiFab> > > ERF::lmask_lev
private

Referenced by ERF_shared().

◆ lon_m

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::lon_m
private

Referenced by ERF_shared().

◆ lsm

LandSurface ERF::lsm
private

◆ lsm_data

amrex::Vector<amrex::Vector<amrex::MultiFab*> > ERF::lsm_data
private

Referenced by ERF_shared().

◆ lsm_data_name

amrex::Vector<std::string> ERF::lsm_data_name
private

◆ lsm_flux

amrex::Vector<amrex::Vector<amrex::MultiFab*> > ERF::lsm_flux
private

Referenced by ERF_shared().

◆ lsm_flux_name

amrex::Vector<std::string> ERF::lsm_flux_name
private

◆ Lwave

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Lwave
private

Referenced by ERF_shared().

◆ Lwave_onegrid

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Lwave_onegrid
private

Referenced by ERF_shared().

◆ m_bc_extdir_vals

amrex::Array<amrex::Array<amrex::Real, AMREX_SPACEDIM*2>, AMREX_SPACEDIM+NBCVAR_max> ERF::m_bc_extdir_vals
private

◆ m_bc_neumann_vals

amrex::Array<amrex::Array<amrex::Real, AMREX_SPACEDIM*2>, AMREX_SPACEDIM+NBCVAR_max> ERF::m_bc_neumann_vals
private

◆ m_bc_nonreflecting

amrex::Array<bool, AMREX_SPACEDIM*2> ERF::m_bc_nonreflecting = {{false}}
private

◆ m_check_int

int ERF::m_check_int = -1
private

◆ m_check_per

amrex::Real ERF::m_check_per = -one
private

◆ m_expand_plotvars_to_unif_rr

bool ERF::m_expand_plotvars_to_unif_rr = false
private

◆ m_forest_drag

amrex::Vector<std::unique_ptr<ForestDrag> > ERF::m_forest_drag
private

Referenced by ERF_shared().

◆ m_plot2d_int_1

int ERF::m_plot2d_int_1 = -1
private

◆ m_plot2d_int_2

int ERF::m_plot2d_int_2 = -1
private

◆ m_plot2d_per_1

amrex::Real ERF::m_plot2d_per_1 = -one
private

◆ m_plot2d_per_2

amrex::Real ERF::m_plot2d_per_2 = -one
private

◆ m_plot3d_int_1

int ERF::m_plot3d_int_1 = -1
private

◆ m_plot3d_int_2

int ERF::m_plot3d_int_2 = -1
private

◆ m_plot3d_per_1

amrex::Real ERF::m_plot3d_per_1 = -one
private

◆ m_plot3d_per_2

amrex::Real ERF::m_plot3d_per_2 = -one
private

◆ m_plot_face_vels

bool ERF::m_plot_face_vels = false
private

◆ m_r2d

std::unique_ptr<ReadBndryPlanes> ERF::m_r2d = nullptr
private

◆ m_subvol_int

amrex::Vector<int> ERF::m_subvol_int
private

◆ m_subvol_per

amrex::Vector<amrex::Real> ERF::m_subvol_per
private

◆ m_SurfaceLayer

std::unique_ptr<SurfaceLayer> ERF::m_SurfaceLayer = nullptr
private

◆ m_w2d

std::unique_ptr<WriteBndryPlanes> ERF::m_w2d = nullptr
private

◆ mapfac

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > ERF::mapfac
private

Referenced by ERF_shared().

◆ max_step

int ERF::max_step = -1
private

◆ metgrid_basic_linear

bool ERF::metgrid_basic_linear {false}
private

◆ metgrid_debug_dry

bool ERF::metgrid_debug_dry {false}
private

◆ metgrid_debug_isothermal

bool ERF::metgrid_debug_isothermal {false}
private

◆ metgrid_debug_msf

bool ERF::metgrid_debug_msf {false}
private

◆ metgrid_debug_psfc

bool ERF::metgrid_debug_psfc {false}
private

◆ metgrid_debug_quiescent

bool ERF::metgrid_debug_quiescent {false}
private

◆ metgrid_force_sfc_k

int ERF::metgrid_force_sfc_k {6}
private

◆ metgrid_interp_theta

bool ERF::metgrid_interp_theta {false}
private

◆ metgrid_order

int ERF::metgrid_order {2}
private

◆ metgrid_proximity

amrex::Real ERF::metgrid_proximity {amrex::Real(500.0)}
private

◆ metgrid_retain_sfc

bool ERF::metgrid_retain_sfc {false}
private

◆ metgrid_use_below_sfc

bool ERF::metgrid_use_below_sfc {true}
private

◆ metgrid_use_sfc

bool ERF::metgrid_use_sfc {true}
private

◆ mf_PSFC

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::mf_PSFC
private

Referenced by ERF_shared().

◆ mg_verbose

int ERF::mg_verbose = 0
staticprivate

◆ micro

std::unique_ptr<Microphysics> ERF::micro
private

◆ mri_integrator_mem

amrex::Vector<std::unique_ptr<MRISplitIntegrator<amrex::Vector<amrex::MultiFab> > > > ERF::mri_integrator_mem
private

Referenced by ERF_shared().

◆ nc_bdy_file

std::string ERF::nc_bdy_file
staticprivate

◆ nc_init_file

Vector< Vector< std::string > > ERF::nc_init_file = {{""}}
staticprivate

◆ nc_low_file

std::string ERF::nc_low_file
staticprivate

◆ ng_dens_hse

int ERF::ng_dens_hse
staticprivate

◆ ng_pres_hse

int ERF::ng_pres_hse
staticprivate

◆ nsubsteps

amrex::Vector<int> ERF::nsubsteps
private

◆ num_boxes_at_level

amrex::Vector<int> ERF::num_boxes_at_level
private

◆ num_files_at_level

amrex::Vector<int> ERF::num_files_at_level
private

◆ nvars_erfbdy

int ERF::nvars_erfbdy {0}
private

◆ output_1d_column

int ERF::output_1d_column = 0
staticprivate

◆ output_bndry_planes

int ERF::output_bndry_planes = 0
staticprivate

◆ pert_interval

int ERF::pert_interval = -1
staticprivate

◆ phys_bc_type

amrex::GpuArray<ERF_BC, AMREX_SPACEDIM*2> ERF::phys_bc_type
private

◆ physbcs_base

amrex::Vector<std::unique_ptr<ERFPhysBCFunct_base> > ERF::physbcs_base
private

Referenced by ERF_shared().

◆ physbcs_cons

amrex::Vector<std::unique_ptr<ERFPhysBCFunct_cons> > ERF::physbcs_cons
private

Referenced by ERF_shared().

◆ physbcs_u

amrex::Vector<std::unique_ptr<ERFPhysBCFunct_u> > ERF::physbcs_u
private

Referenced by ERF_shared().

◆ physbcs_v

amrex::Vector<std::unique_ptr<ERFPhysBCFunct_v> > ERF::physbcs_v
private

Referenced by ERF_shared().

◆ physbcs_w

amrex::Vector<std::unique_ptr<ERFPhysBCFunct_w> > ERF::physbcs_w
private

Referenced by ERF_shared().

◆ plane_sampler

std::unique_ptr<PlaneSampler> ERF::plane_sampler = nullptr
private

◆ plane_sampling_interval

int ERF::plane_sampling_interval = -1
private

◆ plane_sampling_per

amrex::Real ERF::plane_sampling_per = -one
private

◆ plot2d_file_1

std::string ERF::plot2d_file_1 {"plt2d_1_"}
private

◆ plot2d_file_2

std::string ERF::plot2d_file_2 {"plt2d_2_"}
private

◆ plot2d_var_names_1

amrex::Vector<std::string> ERF::plot2d_var_names_1
private

Referenced by ERF_shared().

◆ plot2d_var_names_2

amrex::Vector<std::string> ERF::plot2d_var_names_2
private

Referenced by ERF_shared().

◆ plot3d_file_1

std::string ERF::plot3d_file_1 {"plt_1_"}
private

◆ plot3d_file_2

std::string ERF::plot3d_file_2 {"plt_2_"}
private

◆ plot3d_var_names_1

amrex::Vector<std::string> ERF::plot3d_var_names_1
private

Referenced by ERF_shared().

◆ plot3d_var_names_2

amrex::Vector<std::string> ERF::plot3d_var_names_2
private

Referenced by ERF_shared().

◆ plot_file_on_restart

bool ERF::plot_file_on_restart = true
staticprivate

◆ plot_lsm

bool ERF::plot_lsm = false
private

◆ plot_rad

bool ERF::plot_rad = false
private

◆ plotfile2d_type_1

PlotFileType ERF::plotfile2d_type_1 = PlotFileType::None
staticprivate

◆ plotfile2d_type_2

PlotFileType ERF::plotfile2d_type_2 = PlotFileType::None
staticprivate

◆ plotfile3d_type_1

PlotFileType ERF::plotfile3d_type_1 = PlotFileType::None
staticprivate

◆ plotfile3d_type_2

PlotFileType ERF::plotfile3d_type_2 = PlotFileType::None
staticprivate

◆ pp_inc

amrex::Vector<amrex::MultiFab> ERF::pp_inc
private

Referenced by ERF_shared().

◆ pp_prefix

std::string ERF::pp_prefix {"erf"}

◆ previousCPUTimeUsed

Real ERF::previousCPUTimeUsed = zero
staticprivate

Referenced by getCPUTime().

◆ prob

std::unique_ptr<ProblemBase> ERF::prob = nullptr
private

Referenced by ERF_shared().

◆ profile_int

int ERF::profile_int = -1
private

◆ qfx3_EB

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::qfx3_EB
private

◆ qheating_rates

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::qheating_rates
private

Referenced by ERF_shared().

◆ qmoist

amrex::Vector<amrex::Vector<amrex::MultiFab*> > ERF::qmoist
private

◆ Qr_prim

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Qr_prim
private

Referenced by ERF_shared().

◆ Qv_prim

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Qv_prim
private

Referenced by ERF_shared().

◆ rad

amrex::Vector<std::unique_ptr<IRadiation> > ERF::rad
private

Referenced by ERF_shared().

◆ rad_datalog_int

int ERF::rad_datalog_int = -1
private

Referenced by ERF_shared().

◆ rad_fluxes

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::rad_fluxes
private

◆ real_extrap_w

bool ERF::real_extrap_w {true}
private

◆ real_width

int ERF::real_width {0}
private

◆ ref_tags

Vector< AMRErrorTag > ERF::ref_tags
staticprivate

◆ regrid_int

int ERF::regrid_int = -1
private

◆ regrid_level_0_on_restart

bool ERF::regrid_level_0_on_restart = false
private

◆ restart_chkfile

std::string ERF::restart_chkfile = ""
private

◆ rhoqt_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::rhoqt_src
private

Referenced by ERF_shared().

◆ rhotheta_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::rhotheta_src
private

Referenced by ERF_shared().

◆ rU_new

amrex::Vector<amrex::MultiFab> ERF::rU_new
private

Referenced by ERF_shared().

◆ rU_old

amrex::Vector<amrex::MultiFab> ERF::rU_old
private

Referenced by ERF_shared().

◆ rV_new

amrex::Vector<amrex::MultiFab> ERF::rV_new
private

Referenced by ERF_shared().

◆ rV_old

amrex::Vector<amrex::MultiFab> ERF::rV_old
private

Referenced by ERF_shared().

◆ rW_new

amrex::Vector<amrex::MultiFab> ERF::rW_new
private

Referenced by ERF_shared().

◆ rW_old

amrex::Vector<amrex::MultiFab> ERF::rW_old
private

Referenced by ERF_shared().

◆ sampleline

amrex::Vector<amrex::IntVect> ERF::sampleline
private

Referenced by NumSampleLines(), and SampleLine().

◆ samplelinelog

amrex::Vector<std::unique_ptr<std::fstream> > ERF::samplelinelog
private

◆ samplelinelogname

amrex::Vector<std::string> ERF::samplelinelogname
private

Referenced by SampleLineLogName().

◆ samplepoint

amrex::Vector<amrex::IntVect> ERF::samplepoint
private

Referenced by NumSamplePoints(), and SamplePoint().

◆ sampleptlog

amrex::Vector<std::unique_ptr<std::fstream> > ERF::sampleptlog
private

◆ sampleptlogname

amrex::Vector<std::string> ERF::sampleptlogname
private

Referenced by SamplePointLogName().

◆ SFS_diss_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_diss_lev
private

Referenced by ERF_shared().

◆ SFS_hfx1_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_hfx1_lev
private

Referenced by ERF_shared().

◆ SFS_hfx2_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_hfx2_lev
private

Referenced by ERF_shared().

◆ SFS_hfx3_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_hfx3_lev
private

Referenced by ERF_shared().

◆ SFS_q1fx1_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_q1fx1_lev
private

Referenced by ERF_shared().

◆ SFS_q1fx2_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_q1fx2_lev
private

Referenced by ERF_shared().

◆ SFS_q1fx3_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_q1fx3_lev
private

Referenced by ERF_shared().

◆ SFS_q2fx3_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SFS_q2fx3_lev
private

Referenced by ERF_shared().

◆ sinPhi_m

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::sinPhi_m
private

Referenced by ERF_shared().

◆ SmnSmn_lev

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::SmnSmn_lev
private

Referenced by ERF_shared().

◆ soil_type_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::iMultiFab> > > ERF::soil_type_lev
private

Referenced by ERF_shared().

◆ solverChoice

SolverChoice ERF::solverChoice
staticprivate

◆ sst_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > ERF::sst_lev
private

Referenced by ERF_shared().

◆ start_time

Real ERF::start_time = zero
staticprivate

Referenced by EvolveOneStep().

◆ startCPUTime

Real ERF::startCPUTime = zero
staticprivate

Referenced by getCPUTime().

◆ stop_time

Real ERF::stop_time = std::numeric_limits<amrex::Real>::max()
staticprivate

Referenced by EvolveOneStep().

◆ stretched_dz_d

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::stretched_dz_d
private

Referenced by ERF_shared().

◆ stretched_dz_h

amrex::Vector<amrex::Vector<amrex::Real> > ERF::stretched_dz_h
private

Referenced by ERF_shared().

◆ sub_cfl

Real ERF::sub_cfl = one
staticprivate

◆ subdomains

amrex::Vector<amrex::Vector<amrex::BoxArray> > ERF::subdomains
private

◆ subvol3d_var_names

amrex::Vector<std::string> ERF::subvol3d_var_names
private

◆ subvol_file

std::string ERF::subvol_file {"subvol"}
private

◆ sum_interval

int ERF::sum_interval = -1
staticprivate

◆ sum_per

Real ERF::sum_per = -one
staticprivate

◆ surface_state_1

amrex::Vector<amrex::MultiFab> ERF::surface_state_1

Referenced by ERF_shared().

◆ surface_state_2

amrex::Vector<amrex::MultiFab> ERF::surface_state_2

Referenced by ERF_shared().

◆ surface_state_interp

amrex::Vector<amrex::MultiFab> ERF::surface_state_interp

Referenced by ERF_shared().

◆ t_avg_cnt

amrex::Vector<amrex::Real> ERF::t_avg_cnt
private

Referenced by ERF_shared().

◆ t_new

amrex::Vector<amrex::Real> ERF::t_new
private

Referenced by ERF_shared(), and EvolveOneStep().

◆ t_old

amrex::Vector<amrex::Real> ERF::t_old
private

Referenced by ERF_shared().

◆ Tau

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > ERF::Tau
private

Referenced by ERF_shared().

◆ Tau_corr

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > ERF::Tau_corr
private

Referenced by ERF_shared().

◆ Tau_EB

amrex::Vector<amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > > ERF::Tau_EB
private

Referenced by ERF_shared().

◆ terrain_blanking

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::terrain_blanking
private

Referenced by ERF_shared().

◆ th_bc_data

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::th_bc_data
private

Referenced by ERF_shared().

◆ Theta_prim

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::Theta_prim
private

Referenced by ERF_shared().

◆ thin_xforce

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::thin_xforce
private

Referenced by ERF_shared().

◆ thin_yforce

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::thin_yforce
private

Referenced by ERF_shared().

◆ thin_zforce

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::thin_zforce
private

Referenced by ERF_shared().

◆ timeprecision

const int ERF::timeprecision = 13
private

◆ tot_e_datalog

amrex::Vector<std::unique_ptr<std::fstream> > ERF::tot_e_datalog
private

Referenced by setRecordEnergyDataInfo().

◆ tot_e_datalogname

amrex::Vector<std::string> ERF::tot_e_datalogname
private

◆ tsk_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > ERF::tsk_lev
private

Referenced by ERF_shared().

◆ turbPert

TurbulentPerturbation ERF::turbPert
private

◆ urb_frac_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > ERF::urb_frac_lev
private

Referenced by ERF_shared().

◆ use_datetime

bool ERF::use_datetime = false
private

◆ use_erfbdy

bool ERF::use_erfbdy {false}
private

◆ use_fft

bool ERF::use_fft = false
staticprivate

◆ use_real_time_in_pltname

bool ERF::use_real_time_in_pltname = false
private

◆ vars_new

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::vars_new
private

◆ vars_old

amrex::Vector<amrex::Vector<amrex::MultiFab> > ERF::vars_old
private

Referenced by ERF_shared().

◆ vel_t_avg

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::vel_t_avg
private

Referenced by ERF_shared().

◆ verbose

int ERF::verbose = 0
staticprivate

◆ walldist

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::walldist
private

Referenced by ERF_shared().

◆ weather_forecast_data_1

amrex::Vector<amrex::MultiFab> ERF::weather_forecast_data_1

◆ weather_forecast_data_2

amrex::Vector<amrex::MultiFab> ERF::weather_forecast_data_2

◆ wrf_C1H

std::unique_ptr<amrex::MultiFab> ERF::wrf_C1H
private

◆ wrf_C2H

std::unique_ptr<amrex::MultiFab> ERF::wrf_C2H
private

◆ wrf_MUB

std::unique_ptr<amrex::MultiFab> ERF::wrf_MUB
private

◆ wrf_PHB

std::unique_ptr<amrex::MultiFab> ERF::wrf_PHB
private

◆ write_erfbdy

bool ERF::write_erfbdy {false}
private

◆ xflux_imask

amrex::Vector<std::unique_ptr<amrex::iMultiFab> > ERF::xflux_imask
private

Referenced by ERF_shared().

◆ xvel_bc_data

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::xvel_bc_data
private

Referenced by ERF_shared().

◆ yflux_imask

amrex::Vector<std::unique_ptr<amrex::iMultiFab> > ERF::yflux_imask
private

Referenced by ERF_shared().

◆ yvel_bc_data

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::yvel_bc_data
private

Referenced by ERF_shared().

◆ z_phys_cc

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::z_phys_cc
private

Referenced by ERF_shared().

◆ z_phys_cc_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::z_phys_cc_src
private

Referenced by ERF_shared().

◆ z_phys_nd

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::z_phys_nd
private

Referenced by ERF_shared().

◆ z_phys_nd_new

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::z_phys_nd_new
private

Referenced by ERF_shared().

◆ z_phys_nd_src

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::z_phys_nd_src
private

Referenced by ERF_shared().

◆ z_t_rk

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::z_t_rk
private

Referenced by ERF_shared().

◆ zflux_imask

amrex::Vector<std::unique_ptr<amrex::iMultiFab> > ERF::zflux_imask
private

Referenced by ERF_shared().

◆ zlevels_stag

amrex::Vector<amrex::Vector<amrex::Real> > ERF::zlevels_stag
private

Referenced by ERF_shared().

◆ zmom_crse_rhs

amrex::Vector<amrex::MultiFab> ERF::zmom_crse_rhs
private

Referenced by ERF_shared().

◆ zvel_bc_data

amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real> > ERF::zvel_bc_data
private

Referenced by ERF_shared().


The documentation for this class was generated from the following files: