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

#include <ERF.H>

Inheritance diagram for ERF:
Collaboration diagram for ERF:

Classes

struct  Plot3DScratch
 

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 ()
 
double EvolveOneStep (double time, double dt_request)
 
void ConfigureDriverAtmosToOceanCoupling (bool use_coupling_driver, bool use_two_way_coupling, bool use_state_contract)
 
void SetDriverAtmosToOceanStateContract (bool use_state_contract)
 
void GetOceanToAtmosSurfaceLayout (amrex::BoxArray &ba, amrex::DistributionMapping &dm)
 
void GetOceanToAtmosCellCenterLonLat (const amrex::MultiFab *&lon_cc, const amrex::MultiFab *&lat_cc) const
 
void GetLandMask (const amrex::iMultiFab *&lmask) const
 
bool HasRadiation () const
 
bool HasCloudWater () const
 
void GetFillableAtmosToOceanLanes (bool state_contract, amrex::Vector< int > &can_fill) const
 
void PackAtmosphericStates (amrex::Vector< amrex::MultiFab * > &states, double time, const amrex::Vector< const amrex::MultiFab * > &weight_mf, const amrex::Vector< const amrex::iMultiFab * > &index_mf, int max_stencil_size, const amrex::MultiFab *dst_mskr=nullptr, const amrex::MultiFab *dst_msku=nullptr, const amrex::MultiFab *dst_mskv=nullptr)
 
void ApplyOceanSurfaceState (const amrex::Vector< amrex::MultiFab * > &state, double time, const amrex::iMultiFab *erf_coverage=nullptr)
 
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 double time)
 
void HurricaneTracker (int lev, double 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 (const SolverChoice &solverChoice)
 
void HurricaneEyeTrackerInitial (const SolverChoice &sc, 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 (const SolverChoice &sc, const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, MoistureType moisture_type)
 
void ComputeGlobalMinLocation (const SolverChoice &sc, 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 HurricaneMaxVelTracker (const amrex::Geometry &geom, const amrex::MultiFab &mf_cc_vel, const double &time)
 
void HurricaneMinPressureTracker (MoistureType moisture_type, const amrex::Geometry &geom, const amrex::MultiFab &mf_cons_var, const double &time)
 
void HurricaneTrackerCircle ()
 
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 interp_mapfac_from_coarse (int lev)
 
void interp_psfc_from_coarse (int lev)
 
void interp_land_masks_from_coarse (int lev)
 
bool has_own_init_file (int lev) const
 
void WriteMyEBSurface ()
 
void compute_divergence (int lev, amrex::MultiFab &rhs, amrex::Array< amrex::MultiFab const *, AMREX_SPACEDIM > rho0_u_const, amrex::MultiFab const &mf_mx, amrex::MultiFab const &mf_my, amrex::MultiFab const &mf_vx, amrex::MultiFab const &mf_uy, amrex::MultiFab const &ax_in, amrex::MultiFab const &ay_in, amrex::MultiFab const &dJ_in, amrex::Geometry const &geom_at_lev)
 
void project_initial_velocity (int lev, double time, double dt)
 
void project_momenta (int lev, double l_time, double l_dt, amrex::Vector< amrex::MultiFab > &vars)
 
void project_velocity_tb (int lev, double dt, amrex::Vector< amrex::MultiFab > &vars)
 
void poisson_wall_dist (int lev)
 
void make_subdomains (const amrex::BoxList &ba, amrex::Vector< amrex::BoxArray > &bins)
 Group a list of boxes into subdomains based on adjacency. More...
 
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, double 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 compute_max_buoyancy_gradp_diagnostic (int lev)
 
void check_mesh_type (int lev)
 
bool writeNow (double cur_time, const int nstep, const int plot_int, const double plot_per, const double dt_0, double &last_file_time)
 
void post_timestep (int nstep, double time, double 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, double time, amrex::IntVect cell, amrex::MultiFab &mf)
 
void sample_lines (int lev, double 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)
 
void derive_forcing_profiles_stag (amrex::Gpu::HostVector< amrex::Real > &h_avg_ttend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_wsub, amrex::Gpu::HostVector< amrex::Real > &h_avg_thtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qhtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_tvtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qvtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qcvtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_tnudge, amrex::Gpu::HostVector< amrex::Real > &h_avg_qnudge, amrex::Gpu::HostVector< amrex::Real > &h_avg_unudge, amrex::Gpu::HostVector< amrex::Real > &h_avg_vnudge)
 
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
 
double 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, double dt, double time)
 
void advance_microphysics (int lev, amrex::MultiFab &cons_in, const double &dt_advance, const int &iteration, const double &time)
 
void advance_lsm (int lev, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, const double &time, const double &dt_advance)
 
void advance_radiation (int lev, amrex::MultiFab &cons_in, const double &dt_advance)
 Advance radiation diagnostics and heating rates for one time step. More...
 
void compute_native_shoc_tendencies (int lev, amrex::MultiFab *cons, amrex::MultiFab *xvel, amrex::MultiFab *yvel, amrex::MultiFab *zvel, amrex::Real *w_subsid, amrex::MultiFab *tau13, amrex::MultiFab *tau23, amrex::MultiFab *hfx3, amrex::MultiFab *qfx3, amrex::MultiFab *eddyDiffs, amrex::MultiFab *z_phys_nd, const double &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)
 
bool 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 ResetIntervalMeansAfter3DPlotfileBatch (int plotfiles_written, bool interval_diagnostic_consumed)
 
void ResetIntervalMeans ()
 
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, double 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, double 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 FillPlot2DVars (int lev, const amrex::Vector< std::string > &plot_var_names, const amrex::Vector< std::string > &varnames, const amrex::Vector< plotfile2d::Plotfile2DOutputDescriptor > &output_descriptors, amrex::MultiFab &mf_dst, int ncomp_mf)
 
void FillPlot3DVars (int lev, const amrex::Vector< std::string > &plot_var_names, Plot3DScratch &scratch, amrex::MultiFab &mf_dst, int ncomp_mf, amrex::Real tnew)
 
void BuildPlot3DScratch (const amrex::Vector< std::string > &plot_var_names, Plot3DScratch &scratch, int max_lev=-1, bool sync_solution=true)
 
void init_stations ()
 
void resolve_station_positions ()
 
void resolve_station_stencils ()
 
void sample_stations (amrex::Real time)
 
void flush_stations () 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 init_ibseb ()
 
void ibseb_write_checkpoint (const std::string &checkpointname, int lev) const
 
void ibseb_report (int nstep, amrex::Real time)
 
amrex::Real ibseb_bulk_richardson_height (int lev, const amrex::MultiFab &cons, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel)
 
void ibseb_advance (int lev, amrex::Real time, amrex::Real dt_lev, const amrex::MultiFab &cons, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &zvel)
 
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 double time, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, bool regrid_forces_file_read)
 
void SurfaceDataInterpolation (const int nlevs, const double 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 (const int da_iter)
 
void PerformDataAssimilation (int da_iter)
 
void SetDirsForPlotfilesAndCheckpointsForDA (const int da_iter, const int ens_no)
 
void GetEnsembleCheckpointName (int da_iter, int ens_no)
 
void init_custom (int lev)
 
void fill_from_bndryregs (const amrex::Vector< amrex::MultiFab * > &mfs, double 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-to-coarse average of RhoTheta and the active moist conserved components using detJ/mfac weighting, reusing AverageDownTo with perturbational-state and momentum handling disabled. More...
 
void WriteCheckpointFile () const
 
void ReadCheckpointFile ()
 
void ReadVelsOnlyFromCheckpointFile (int lev_to_fill, std::string &chkfile)
 
void ReadCheckpointFileSurfaceLayer ()
 
void fill_terrain_surface (int lev, amrex::MultiFab &z_phys, double elapsed_time)
 
void init_zphys (int lev, double elapsed_time)
 
void remake_zphys (int lev, amrex::Real time, std::unique_ptr< amrex::MultiFab > &temp_zphys_nd)
 
void update_terrain_arrays (int lev)
 
void writeJobInfo (const std::string &dir, erf_provenance::ArtifactType artifact_type, int artifact_step, double artifact_time_seconds) const
 

Static Public Member Functions

static bool is_it_time_for_action (int nstep, double time, double dt, int action_interval, double 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"}
 
std::set< std::string > user_specified_inputs
 
bool m_driver_has_atm2ocn_coupling = false
 
bool m_driver_uses_two_way_coupling = false
 
bool m_driver_atm2ocn_uses_state_contract = true
 
std::unique_ptr< amrex::MultiFab > m_coupled_sst
 
std::unique_ptr< amrex::iMultiFab > m_coupled_sst_valid
 
bool m_warned_coupled_sst_declined = false
 

Private Member Functions

void ReadParameters ()
 
void ParameterSanityChecks ()
 
bool has_surface_layer_inputs (const std::string &prefix) const
 
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 &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 double dt)
 
void turbPert_amplitude (const int lev)
 
void turbPert_amplitude_w (const int lev)
 
void initialize_integrator (int lev, amrex::MultiFab &cons_mf, amrex::MultiFab &vel_mf)
 
void make_physbcs (int lev)
 
void make_lsm_at_level (int lev, bool from_regrid[[maybe_unused]]=false)
 
void initializeMicrophysics (const int &)
 
void initializeEB ()
 
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, double 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, double time)
 
void timeStep (int lev, double time, int iteration)
 
void Advance (int lev, double time, double dt_lev, int iteration, int ncycle)
 
void initHSE ()
 Initialize HSE. More...
 
void initHSE (int lev)
 
void interp_base_state_from_coarse (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 canonicalizePlot3DVariables (amrex::Vector< std::string > &plot_var_names)
 
void appendPlot3DVariables (const amrex::Vector< std::string > &plot_var_names, amrex::Vector< std::string > &a_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, double time, const amrex::Geometry &geom)
 
void fill_rhs (amrex::MultiFab &rhs_mf, const amrex::MultiFab &state_mf, double time, const amrex::Geometry &geom)
 
void define_column_kextent (int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
 
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 bool grids_are_split_in_z (const amrex::BoxArray &ba)
 
static AMREX_FORCE_INLINE int ComputeGhostCells (const SolverChoice &sc)
 
static double 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
 
amrex::Vector< amrex::TableData< amrex::Real, 1 > > r_plane_avg
 
amrex::Vector< amrex::TableData< amrex::Real, 1 > > t_plane_avg
 
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< int > rad_interp_from_coarse_pending
 
amrex::Vector< double > t_new
 
amrex::Vector< double > t_old
 
amrex::Vector< double > dt
 
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< double > t_avg_cnt
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > interval_means
 
amrex::Vector< double > t_mean_cnt
 
int mean_vars_time_reset_done {0}
 
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
 
std::unique_ptr< CloudChamberBudgetcloud_chamber_budget
 
erf_cloud_chamber::Config cloud_chamber_config
 
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
 
LargeScaleForcingData lsf
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > nudge_data
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lsf_data
 
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
 
TwoStreamRadiation two_stream_rad
 
amrex::Vector< std::unique_ptr< ShocDriver > > native_shoc_driver
 
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
 
IBSEBParams ibseb_params
 
amrex::Vector< std::unique_ptr< IBFaceSet > > m_ibseb
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking_xface
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking_yface
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking_zface
 
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< amrex::Array< erf_wall_scalar_bc::WallScalarBCIntent, AMREX_SPACEDIM *2 >, NBCVAR_maxm_wall_scalar_bc_intent {}
 
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< double > last_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 = ""
 
erf_provenance::ExecutionProvenance execution_provenance
 
amrex::Vector< double > fixed_dt
 
amrex::Vector< double > fixed_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< double > m_subvol_per
 
double m_plot3d_per_1 = -1.0
 
double m_plot3d_per_2 = -1.0
 
double m_plot2d_per_1 = -1.0
 
double m_plot2d_per_2 = -1.0
 
bool m_plot_face_vels = false
 
bool m_plot_face_terrain_blanking = false
 
bool plot_lsm = false
 
int profile_int = -1
 
bool destag_profiles = true
 
std::string check_file {"chk"}
 
int m_check_int = -1
 
double m_check_per = -1.0
 
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
 
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
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > column_kextent
 
BaseStateParams wrf_bsp
 
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_RDNW
 
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
 
amrex::Vector< std::unique_ptr< SurfaceLayer > > m_SurfaceLayer
 
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
 
int station_sampling_interval = -1
 
amrex::Real station_sampling_per = -one
 
std::unique_ptr< StationSamplerstation_sampler = nullptr
 
amrex::Vector< std::string > station_vars_3d
 
amrex::Vector< std::string > station_vars_2d
 
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 double last_plot3d_file_time_1 = 0.0
 
static double last_plot3d_file_time_2 = 0.0
 
static double last_plot2d_file_time_1 = 0.0
 
static double last_plot2d_file_time_2 = 0.0
 
static double last_check_file_time = 0.0
 
static bool plot_file_on_restart = true
 
static double start_time = 0.0
 
static double stop_time = std::numeric_limits<double>::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 double dt_max_initial = static_cast<double>(bogus_large_value)
 
static double dt_max = 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 double sum_per = -1.0
 
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 constexpr int column_kextent_lo_sentinel = std::numeric_limits<int>::min()/2
 
static constexpr int column_kextent_hi_sentinel = std::numeric_limits<int>::max()/2
 
static int output_1d_column = 0
 
static int column_interval = -1
 
static double column_per = -1.0
 
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 double bndry_output_planes_per = -1.0
 
static double bndry_output_planes_start_time = 0.0
 
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::Vector< std::string > ref_tag_indicator_names
 
static double startCPUTime = 0.0
 
static double previousCPUTimeUsed = 0.0
 

Detailed Description

Main class in ERF code, instantiated from main.cpp

Constructor & Destructor Documentation

◆ ERF() [1/3]

ERF::ERF ( )
19 {
20  ERF_shared();
21 }
void ERF_shared()
Definition: ERF_Constructors.cpp:49

◆ ~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,
double  time,
double  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
22 {
23  BL_PROFILE("ERF::Advance()");
24 
25  // We must swap the pointers so the previous step's "new" is now this step's "old"
26  std::swap(vars_old[lev], vars_new[lev]);
27 
28  // Surface energy balance on the building faces, with the state at the start of the step
29  ibseb_advance(lev, time, dt_lev, vars_old[lev][Vars::cons],
31 
32  MultiFab& S_old = vars_old[lev][Vars::cons];
33  MultiFab& S_new = vars_new[lev][Vars::cons];
34 
35  MultiFab& U_old = vars_old[lev][Vars::xvel];
36  MultiFab& V_old = vars_old[lev][Vars::yvel];
37  MultiFab& W_old = vars_old[lev][Vars::zvel];
38 
39  MultiFab& U_new = vars_new[lev][Vars::xvel];
40  MultiFab& V_new = vars_new[lev][Vars::yvel];
41  MultiFab& W_new = vars_new[lev][Vars::zvel];
42 
43  // We need to set these because otherwise in the first call to erf_advance we may
44  // read uninitialized data on ghost values in setting the bc's on the velocities
45  U_new.setVal(bogus_large_value,U_new.nGrowVect());
46  V_new.setVal(bogus_large_value,V_new.nGrowVect());
47  W_new.setVal(bogus_large_value,W_new.nGrowVect());
48 
49  //
50  // NOTE: the momenta here are not fillpatched (they are only used as scratch space)
51  // If lev == 0 we have already FillPatched this in ERF::TimeStep
52  //
53  if (lev > 0) {
54  // Set ghost cells to bogus values so they aren't uninitialized
55  W_old.setBndry(bogus_large_value);
56  FillPatchFineLevel(lev, time, {&S_old, &U_old, &V_old, &W_old},
57  {&S_old, &rU_old[lev], &rV_old[lev], &rW_old[lev]},
58  base_state[lev], base_state[lev]);
59  }
60 
61  //
62  // So we must convert the fillpatched to momenta, including the ghost values
63  //
64  const MultiFab* c_vfrac = nullptr;
65  if (solverChoice.terrain_type == TerrainType::EB) {
66  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
67  }
68 
69  VelocityToMomentum(U_old, rU_old[lev].nGrowVect(),
70  V_old, rV_old[lev].nGrowVect(),
71  W_old, rW_old[lev].nGrowVect(),
72  S_old, rU_old[lev], rV_old[lev], rW_old[lev],
73  Geom(lev).Domain(),
74  domain_bcs_type, c_vfrac);
75 
76  // Update the inflow perturbation update time and amplitude
78  {
79  turbPert.calc_tpi_update(lev, dt_lev, U_old, V_old, S_old);
80  }
81 
82  // If PerturbationType::Direct or CPM is selected, directly add the computed perturbation
83  // on the conserved field
85  {
86  if (solverChoice.use_wvel_perturbation(lev)) { // CPM_W
87  auto m_ixtype = W_old.boxArray().ixType();
88  for (MFIter mfi(W_old,TileNoZ()); mfi.isValid(); ++mfi) {
89  Box bx = mfi.tilebox();
90  const Array4<Real> &cell_data = W_old.array(mfi);
91  const Array4<const Real> &pert_cell = turbPert.pb_cell[lev].array(mfi);
92  turbPert.apply_tpi(lev, bx, -1, m_ixtype, cell_data, pert_cell);
93  }
94  } else {
95  auto m_ixtype = S_old.boxArray().ixType(); // Conserved term
96  for (MFIter mfi(S_old,TileNoZ()); mfi.isValid(); ++mfi) {
97  Box bx = mfi.tilebox();
98  const Array4<Real> &cell_data = S_old.array(mfi);
99  const Array4<const Real> &pert_cell = turbPert.pb_cell[lev].array(mfi);
100  turbPert.apply_tpi(lev, bx, RhoTheta_comp, m_ixtype, cell_data, pert_cell);
101  }
102  }
103  }
104 
105  // configure SurfaceLayer params if needed
106  bool updated_prim = false;
107  for (OrientationIter oit; oit; ++oit) {
108  Orientation ori = oit();
109  if (phys_bc_type[ori] == ERF_BC::surface_layer && m_SurfaceLayer[ori]) {
110  if (!updated_prim) {
111  // This only needs to be done once
112  IntVect ng = Theta_prim[lev]->nGrowVect();
113  MultiFab::Copy( *Theta_prim[lev], S_old, RhoTheta_comp, 0, 1, ng);
114  MultiFab::Divide(*Theta_prim[lev], S_old, Rho_comp , 0, 1, ng);
115  if (solverChoice.moisture_type != MoistureType::None) {
116  ng = Qv_prim[lev]->nGrowVect();
117 
118  MultiFab::Copy( *Qv_prim[lev], S_old, RhoQ1_comp, 0, 1, ng);
119  MultiFab::Divide(*Qv_prim[lev], S_old, Rho_comp , 0, 1, ng);
120 
121  if (solverChoice.moisture_indices.qr > -1) {
122  MultiFab::Copy( *Qr_prim[lev], S_old, solverChoice.moisture_indices.qr, 0, 1, ng);
123  MultiFab::Divide(*Qr_prim[lev], S_old, Rho_comp , 0, 1, ng);
124  } else {
125  Qr_prim[lev]->setVal(0);
126  }
127  }
128  updated_prim = true;
129  }
130  // NOTE: std::swap above causes the field ptrs to be out of date.
131  // Reassign the field ptrs for MAC avg computation.
132  m_SurfaceLayer[ori]->update_mac_ptrs(lev, vars_old, Theta_prim, Qv_prim, Qr_prim);
133  m_SurfaceLayer[ori]->update_pblh(lev, vars_old, z_phys_cc[lev].get(),
135 
136 #ifdef ERF_USE_NETCDF
137  double elapsed_time_since_start_low = time + (start_time - start_low_time);
138 #else
139  double elapsed_time_since_start_low = time;
140 #endif
141  m_SurfaceLayer[ori]->update_fluxes(lev, time, elapsed_time_since_start_low,
142  S_old, z_phys_nd[lev], walldist[lev]);
143  }
144  }
145 
146 #if defined(ERF_USE_WINDFARM)
147  // **************************************************************************************
148  // Update the windfarm sources
149  // **************************************************************************************
150  if (solverChoice.windfarm_type != WindFarmType::None) {
151  advance_windfarm(Geom(lev), dt_lev, S_old,
152  U_old, V_old, W_old, vars_windfarm[lev],
153  Nturb[lev], SMark[lev], time);
154  }
155 
156 #endif
157 
158  // **************************************************************************************
159  // Update the radiation sources with the "old" state
160  // **************************************************************************************
161  advance_radiation(lev, S_old, dt_lev);
162 
163  // **************************************************************************************
164  // Update the "old" state using SHOC
165  // **************************************************************************************
166  if (solverChoice.turbChoice[lev].uses_shoc_family()) {
167  // Get SFC fluxes from SurfaceLayer
168  if (m_SurfaceLayer[Orientation::zlo()]) {
169  Vector<const MultiFab*> mfs = {&S_old, &U_old, &V_old, &W_old};
170  m_SurfaceLayer[Orientation::zlo()]->impose_SurfaceLayer_bcs(lev, mfs, Tau[lev],
171  SFS_hfx1_lev[lev].get() , SFS_hfx2_lev[lev].get() , SFS_hfx3_lev[lev].get(),
172  SFS_q1fx1_lev[lev].get(), SFS_q1fx2_lev[lev].get(), SFS_q1fx3_lev[lev].get(),
173  z_phys_nd[lev].get());
174  }
175 
176  // Apply SHOC before the dycore so it sees a coherent state.
177  Real* w_sub = (solverChoice.custom_w_subsidence) ? d_w_subsid[lev].data() : nullptr;
178  if (solverChoice.turbChoice[lev].uses_eamxx_shoc()) {
179 #ifdef ERF_USE_EAMXX_SHOC
180  compute_shoc_tendencies(lev, &S_old, &U_old, &V_old, &W_old, w_sub,
181  Tau[lev][TauType::tau13].get(), Tau[lev][TauType::tau23].get(),
182  SFS_hfx3_lev[lev].get() , SFS_q1fx3_lev[lev].get() ,
183  eddyDiffs_lev[lev].get() , z_phys_nd[lev].get() ,
184  dt_lev);
185 #endif
186  } else if (solverChoice.turbChoice[lev].uses_native_shoc()) {
187  compute_native_shoc_tendencies(lev, &S_old, &U_old, &V_old, &W_old, w_sub,
188  Tau[lev][TauType::tau13].get(), Tau[lev][TauType::tau23].get(),
189  SFS_hfx3_lev[lev].get() , SFS_q1fx3_lev[lev].get() ,
190  eddyDiffs_lev[lev].get() , z_phys_nd[lev].get() ,
191  dt_lev);
192 
193  if (native_shoc_driver[lev]) {
194  // Native SHOC updates the old-time state before the dycore reads it.
195  // Re-fill the updated state, velocities, and momenta now so the
196  // pre-dycore checks and strain calculation see coherent fields.
197  Vector<MultiFab*> mfs_vel = {&S_old, &U_old, &V_old, &W_old};
198  if (lev == 0) {
199  FillPatchCrseLevel(lev, time, mfs_vel, false);
200  VelocityToMomentum(U_old, rU_old[lev].nGrowVect(),
201  V_old, rV_old[lev].nGrowVect(),
202  W_old, rW_old[lev].nGrowVect(),
203  S_old, rU_old[lev], rV_old[lev], rW_old[lev],
204  Geom(lev).Domain(),
205  domain_bcs_type, c_vfrac);
206  } else {
207  Vector<MultiFab*> mfs_mom = {&S_old, &rU_old[lev], &rV_old[lev], &rW_old[lev]};
208  FillPatchFineLevel(lev, time, mfs_vel, mfs_mom,
209  base_state[lev], base_state[lev],
210  true, false);
211  }
212  }
213  }
214  }
215 
216  const BoxArray& ba = S_old.boxArray();
217  const DistributionMapping& dm = S_old.DistributionMap();
218 
219  int nvars = S_old.nComp();
220 
221  // Source array for conserved cell-centered quantities -- this will be filled
222  // in the call to make_sources in ERF_TI_slow_rhs_pre.H
223  MultiFab cc_source(ba,dm,nvars,1); cc_source.setVal(0);
224 
225  // Source arrays for momenta -- these will be filled
226  // in the call to make_mom_sources in ERF_TI_slow_rhs_pre.H
227  BoxArray ba_x(ba); ba_x.surroundingNodes(0);
228  MultiFab xmom_source(ba_x,dm,1,1); xmom_source.setVal(0);
229 
230  BoxArray ba_y(ba); ba_y.surroundingNodes(1);
231  MultiFab ymom_source(ba_y,dm,1,1); ymom_source.setVal(0);
232 
233  BoxArray ba_z(ba); ba_z.surroundingNodes(2);
234  MultiFab zmom_source(ba_z,dm,1,1); zmom_source.setVal(0);
235  MultiFab buoyancy(ba_z,dm,1,1); buoyancy.setVal(0);
236 
237  amrex::Vector<MultiFab> state_old;
238  amrex::Vector<MultiFab> state_new;
239 
240  // **************************************************************************************
241  // Here we define state_old and state_new which are to be advanced
242  // **************************************************************************************
243  // Initial solution
244  // Note that "old" and "new" here are relative to each RK stage.
245  state_old.push_back(MultiFab(S_old , amrex::make_alias, 0, nvars)); // cons
246  state_old.push_back(MultiFab(rU_old[lev], amrex::make_alias, 0, 1)); // xmom
247  state_old.push_back(MultiFab(rV_old[lev], amrex::make_alias, 0, 1)); // ymom
248  state_old.push_back(MultiFab(rW_old[lev], amrex::make_alias, 0, 1)); // zmom
249 
250  // Final solution
251  // state_new at the end of the last RK stage holds the t^{n+1} data
252  state_new.push_back(MultiFab(S_new , amrex::make_alias, 0, nvars)); // cons
253  state_new.push_back(MultiFab(rU_new[lev], amrex::make_alias, 0, 1)); // xmom
254  state_new.push_back(MultiFab(rV_new[lev], amrex::make_alias, 0, 1)); // ymom
255  state_new.push_back(MultiFab(rW_new[lev], amrex::make_alias, 0, 1)); // zmom
256 
257  // **************************************************************************************
258  // Tests on the reasonableness of the solution before the dycore
259  // **************************************************************************************
260  // Test for NaNs after dycore
261  if (check_for_nans > 1) {
262  if (verbose > 1) {
263  amrex::Print() << "Testing old state and vels for NaNs before dycore" << std::endl;
264  }
265  check_state_for_nans(S_old);
266  check_vels_for_nans(rU_old[lev],rV_old[lev],rW_old[lev]);
267  }
268 
269  // We only test on low temp if we have a moisture model because we are protecting against
270  // the test on low temp inside the moisture models
271  if (solverChoice.moisture_type != MoistureType::None) {
272  if (verbose > 1) {
273  amrex::Print() << "Testing on low temperature before dycore" << std::endl;
274  }
275  check_for_low_temp(S_old);
276  } else {
277  if (verbose > 1) {
278  amrex::Print() << "Testing on negative temperature before dycore" << std::endl;
279  }
281  }
282 
283  // **************************************************************************************
284  // Update the dycore
285  // **************************************************************************************
286  advance_dycore(lev, state_old, state_new,
287  U_old, V_old, W_old,
288  U_new, V_new, W_new,
289  cc_source, xmom_source, ymom_source, zmom_source, buoyancy,
290  Geom(lev), dt_lev, time);
291 
292  // **************************************************************************************
293  // Tests on the reasonableness of the solution after the dycore
294  // **************************************************************************************
295  // Test for NaNs after dycore
296  if (check_for_nans > 0) {
297  if (verbose > 1) {
298  amrex::Print() << "Testing new state and vels for NaNs after dycore" << std::endl;
299  }
300  check_state_for_nans(S_new);
301  check_vels_for_nans(rU_new[lev],rV_new[lev],rW_new[lev]);
302  }
303 
304  // We only test on low temp if we have a moisture model because we are protecting against
305  // the test on low temp inside the moisture models
306  if (solverChoice.moisture_type != MoistureType::None) {
307  if (verbose > 1) {
308  amrex::Print() << "Testing on low temperature after dycore" << std::endl;
309  }
310  check_for_low_temp(S_new);
311  } else {
312  // Otherwise we will test on negative (rhotheta) coming out of the dycore
313  if (verbose > 1) {
314  amrex::Print() << "Testing on negative temperature after dycore" << std::endl;
315  }
317  }
318 
319  // **************************************************************************************
320  // Update the microphysics (moisture)
321  // **************************************************************************************
323  {
324  // S_new ghost cells are stale after the dycore RK stages; refresh
325  // them before microphysics (Lagrangian particle interpolation reads
326  // the ghost region for cells along level boundaries).
327  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
328  if (lev == 0) {
329  FillPatchCrseLevel(lev, t_new[lev],
330  {&S_new, &U_new, &V_new, &W_new},
331  /*cons_only=*/true);
332  } else {
333  FillPatchFineLevel(lev, t_new[lev],
334  {&S_new, &U_new, &V_new, &W_new},
335  {&S_new, &rU_new[lev], &rV_new[lev], &rW_new[lev]},
336  base_state[lev], base_state[lev],
337  /*fillset=*/true, /*cons_only=*/true);
338  }
339  }
340 
341  advance_microphysics(lev, S_new, dt_lev, iteration, time);
342 
343  // Test for NaNs after microphysics
344  if (check_for_nans > 0) {
345  amrex::Print() << "Testing new state for NaNs after advance_microphysics" << std::endl;
346  check_state_for_nans(S_new);
347  }
348  }
349 
350  // **************************************************************************************
351  // Update the land surface model
352  // **************************************************************************************
353  double time_at_end_of_step = time+dt_lev;
354  advance_lsm(lev, S_new, U_new, V_new, time_at_end_of_step, dt_lev);
355 
356 #ifdef ERF_USE_PARTICLES
357  // **************************************************************************************
358  // Update the particle positions
359  // **************************************************************************************
360  evolveTracers(lev, dt_lev, vars_new, z_phys_nd);
361 #endif
362 
363  // ***********************************************************************************************
364  // Impose domain boundary conditions here so that in FillPatching the fine data we won't
365  // need to re-fill these
366  // ***********************************************************************************************
367  if (lev < finest_level) {
368  IntVect ngvect_vels = vars_new[lev][Vars::xvel].nGrowVect();
370  0,vars_new[lev][Vars::cons].nComp(),
371  vars_new[lev][Vars::cons].nGrowVect(),time,BCVars::cons_bc,true);
372  (*physbcs_u[lev])(vars_new[lev][Vars::xvel], vars_new[lev][Vars::xvel], vars_new[lev][Vars::yvel],
373  ngvect_vels,time,BCVars::xvel_bc,true);
374  (*physbcs_v[lev])(vars_new[lev][Vars::yvel], vars_new[lev][Vars::xvel], vars_new[lev][Vars::yvel],
375  ngvect_vels,time,BCVars::yvel_bc,true);
376  (*physbcs_w[lev])(vars_new[lev][Vars::zvel], vars_new[lev][Vars::xvel], vars_new[lev][Vars::yvel],
377  ngvect_vels,time,BCVars::zvel_bc,true);
378  }
379 
380  // **************************************************************************************
381  // Register old and new coarse data if we are at a level less than the finest level
382  // **************************************************************************************
383  if (lev < finest_level) {
384  if (cf_width > 0) {
385  // We must fill the ghost cells of these so that the parallel copy works correctly
386  state_old[IntVars::cons].FillBoundary(geom[lev].periodicity());
387  state_new[IntVars::cons].FillBoundary(geom[lev].periodicity());
388  FPr_c[lev].RegisterCoarseData({&state_old[IntVars::cons], &state_new[IntVars::cons]},
389  {time, time+dt_lev});
390  }
391 
392  if (cf_width >= 0) {
393  // We must fill the ghost cells of these so that the parallel copy works correctly
394  state_old[IntVars::xmom].FillBoundary(geom[lev].periodicity());
395  state_new[IntVars::xmom].FillBoundary(geom[lev].periodicity());
396  FPr_u[lev].RegisterCoarseData({&state_old[IntVars::xmom], &state_new[IntVars::xmom]},
397  {time, time+dt_lev});
398 
399  state_old[IntVars::ymom].FillBoundary(geom[lev].periodicity());
400  state_new[IntVars::ymom].FillBoundary(geom[lev].periodicity());
401  FPr_v[lev].RegisterCoarseData({&state_old[IntVars::ymom], &state_new[IntVars::ymom]},
402  {time, time+dt_lev});
403 
404  state_old[IntVars::zmom].FillBoundary(geom[lev].periodicity());
405  state_new[IntVars::zmom].FillBoundary(geom[lev].periodicity());
406  FPr_w[lev].RegisterCoarseData({&state_old[IntVars::zmom], &state_new[IntVars::zmom]},
407  {time, time+dt_lev});
408  }
409 
410  //
411  // Now create a MultiFab that holds (S_new - S_old) / dt from the coarse level interpolated
412  // on to the coarse/fine boundary at the fine resolution
413  //
414  Interpolater* mapper_f = &face_cons_linear_interp;
415 
416  // PhysBCFunctNoOp null_bc;
417  // MultiFab tempx(vars_new[lev+1][Vars::xvel].boxArray(),vars_new[lev+1][Vars::xvel].DistributionMap(),1,0);
418  // tempx.setVal(0);
419  // xmom_crse_rhs[lev+1].setVal(0);
420  // FPr_u[lev].FillSet(tempx , time , null_bc, domain_bcs_type);
421  // FPr_u[lev].FillSet(xmom_crse_rhs[lev+1], time+dt_lev, null_bc, domain_bcs_type);
422  // MultiFab::Subtract(xmom_crse_rhs[lev+1],tempx,0,0,1,IntVect{0});
423  // xmom_crse_rhs[lev+1].mult(one/dt_lev,0,1,0);
424 
425  // MultiFab tempy(vars_new[lev+1][Vars::yvel].boxArray(),vars_new[lev+1][Vars::yvel].DistributionMap(),1,0);
426  // tempy.setVal(0);
427  // ymom_crse_rhs[lev+1].setVal(0);
428  // FPr_v[lev].FillSet(tempy , time , null_bc, domain_bcs_type);
429  // FPr_v[lev].FillSet(ymom_crse_rhs[lev+1], time+dt_lev, null_bc, domain_bcs_type);
430  // MultiFab::Subtract(ymom_crse_rhs[lev+1],tempy,0,0,1,IntVect{0});
431  // ymom_crse_rhs[lev+1].mult(one/dt_lev,0,1,0);
432 
433  MultiFab temp_state(zmom_crse_rhs[lev+1].boxArray(),zmom_crse_rhs[lev+1].DistributionMap(),1,0);
434  InterpFromCoarseLevel(temp_state, IntVect{0}, IntVect{0}, state_old[IntVars::zmom], 0, 0, 1,
435  geom[lev], geom[lev+1], refRatio(lev), mapper_f, domain_bcs_type, BCVars::zvel_bc);
436  InterpFromCoarseLevel(zmom_crse_rhs[lev+1], IntVect{0}, IntVect{0}, state_new[IntVars::zmom], 0, 0, 1,
437  geom[lev], geom[lev+1], refRatio(lev), mapper_f, domain_bcs_type, BCVars::zvel_bc);
438  MultiFab::Subtract(zmom_crse_rhs[lev+1],temp_state,0,0,1,IntVect{0});
439 
440  Real inv_dt = static_cast<Real>(one/dt_lev);
441  zmom_crse_rhs[lev+1].mult(inv_dt,0,1,0);
442  }
443 
444  // ***********************************************************************************************
445  // Update the time averaged velocities if they are requested
446  // ***********************************************************************************************
448  Time_Avg_Vel_atCC(dt[lev], t_avg_cnt[lev], vel_t_avg[lev].get(), U_new, V_new, W_new);
449  }
450 
451  // ***********************************************************************************************
452  // Two-stream radiation, post-dycore call: reports the cached flux
453  // diagnostics and advances the force-restore surface state by dt_lev.
454  // No column sweep runs here; that happened in advance_radiation above.
455  // ***********************************************************************************************
456  if (solverChoice.rad_type == RadiationType::TwoStream) {
457 #ifdef ERF_USE_NETCDF
458  const MultiFab* lat_ptr = lat_m[lev].get();
459  const MultiFab* lon_ptr = lon_m[lev].get();
460 #else
461  const MultiFab* lat_ptr = nullptr;
462  const MultiFab* lon_ptr = nullptr;
463 #endif
464  const MultiFab* t_surf = (m_SurfaceLayer[Orientation::zlo()])
465  ? m_SurfaceLayer[Orientation::zlo()]->get_t_surf(lev)
466  : nullptr;
467  two_stream_rad.advance(lev, iteration, time + dt_lev, dt_lev, "post_dycore",
468  vars_old[lev][Vars::cons], z_phys_nd[lev].get(), geom[lev],
469  lsm, qheating_rates[lev].get(), rad_fluxes[lev].get(),
470  t_surf, lat_ptr, lon_ptr,
471  time + dt_lev + start_time, use_datetime);
472  }
474  // The interval window is shared by all AMR levels. Reset it before
475  // accumulating the first sample whose step starts at or beyond the
476  // configured reset time. The restart reader restores the flags when
477  // that time has already passed, so this is a one-shot transition.
478  if (solverChoice.mean_vars_reset_mode == "time" &&
479  time >= static_cast<double>(solverChoice.mean_vars_reset_time)) {
480  if (mean_vars_time_reset_done == 0) {
483  }
484  }
485 
487  U_new, V_new, W_new, S_new);
488  }
489 
490 }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:17
@ tau23
Definition: ERF_DataStruct.H:40
@ tau13
Definition: ERF_DataStruct.H:40
@ nvars
Definition: ERF_DataStruct.H:179
#define Rho_comp
Definition: ERF_IndexDefines.H:39
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45
@ surface_layer
pp get("wavelength", wavelength)
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
void Accumulate_Interval_Means(double dt_d, double &t_mean_cnt, MultiFab *interval_means, MultiFab &xvel, MultiFab &yvel, MultiFab &zvel, MultiFab &cons)
Definition: ERF_TimeAvgVel.cpp:53
void Time_Avg_Vel_atCC(double dt_d, double &t_avg_cnt, MultiFab *vel_t_avg, MultiFab &xvel, MultiFab &yvel, MultiFab &zvel)
Definition: ERF_TimeAvgVel.cpp:10
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:1266
amrex::Vector< std::unique_ptr< amrex::MultiFab > > walldist
Definition: ERF.H:1402
void check_vels_for_nans(amrex::MultiFab const &xvel, amrex::MultiFab const &yvel, amrex::MultiFab const &zvel)
Definition: ERF.cpp:3687
amrex::Vector< ERFFillPatcher > FPr_u
Definition: ERF.H:1335
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx3_lev
Definition: ERF.H:1358
amrex::Vector< double > t_new
Definition: ERF.H:1217
amrex::Vector< amrex::Vector< amrex::MultiFab > > vars_new
Definition: ERF.H:1225
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx3_lev
Definition: ERF.H:1356
amrex::Vector< std::unique_ptr< ShocDriver > > native_shoc_driver
Definition: ERF.H:1322
amrex::Vector< ERFFillPatcher > FPr_v
Definition: ERF.H:1336
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx1_lev
Definition: ERF.H:1356
eb_ const & get_eb(int lev) const noexcept
Definition: ERF.H:2161
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_cons > > physbcs_cons
Definition: ERF.H:1253
amrex::Vector< double > dt
Definition: ERF.H:1219
void advance_lsm(int lev, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, const double &time, const double &dt_advance)
Definition: ERF_AdvanceLSM.cpp:5
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_cc
Definition: ERF.H:1372
amrex::Vector< std::unique_ptr< amrex::MultiFab > > eddyDiffs_lev
Definition: ERF.H:1342
static SolverChoice solverChoice
Definition: ERF.H:1646
int mean_vars_time_reset_done
Definition: ERF.H:1239
amrex::Vector< ERFFillPatcher > FPr_c
Definition: ERF.H:1334
TwoStreamRadiation two_stream_rad
Definition: ERF.H:1316
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > Tau
Definition: ERF.H:1340
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vel_t_avg
Definition: ERF.H:1232
static int verbose
Definition: ERF.H:1681
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_w > > physbcs_w
Definition: ERF.H:1256
amrex::Vector< amrex::MultiFab > base_state
Definition: ERF.H:1413
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Qv_prim
Definition: ERF.H:1261
amrex::Vector< double > t_avg_cnt
Definition: ERF.H:1233
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx2_lev
Definition: ERF.H:1358
amrex::Vector< amrex::MultiFab > rV_new
Definition: ERF.H:1268
amrex::Vector< amrex::BCRec > domain_bcs_type
Definition: ERF.H:1429
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Qr_prim
Definition: ERF.H:1262
void ibseb_advance(int lev, amrex::Real time, amrex::Real dt_lev, const amrex::MultiFab &cons, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &zvel)
Definition: ERF_IBSEB.cpp:145
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_u > > physbcs_u
Definition: ERF.H:1254
amrex::Vector< amrex::MultiFab > rU_old
Definition: ERF.H:1265
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Theta_prim
Definition: ERF.H:1260
static int check_for_nans
Definition: ERF.H:1685
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_v > > physbcs_v
Definition: ERF.H:1255
void advance_microphysics(int lev, amrex::MultiFab &cons_in, const double &dt_advance, const int &iteration, const double &time)
Definition: ERF_AdvanceMicrophysics.cpp:5
amrex::Vector< double > t_mean_cnt
Definition: ERF.H:1237
void check_state_for_nans(amrex::MultiFab const &S)
Definition: ERF.cpp:3620
bool use_datetime
Definition: ERF.H:1497
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd
Definition: ERF.H:1371
void ResetIntervalMeans()
Definition: ERF.cpp:358
amrex::Vector< amrex::MultiFab > rW_new
Definition: ERF.H:1270
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lon_m
Definition: ERF.H:1150
amrex::Vector< std::unique_ptr< SurfaceLayer > > m_SurfaceLayer
Definition: ERF.H:1846
void FillPatchCrseLevel(int lev, double time, const amrex::Vector< amrex::MultiFab * > &mfs_vel, bool cons_only=false)
Definition: ERF_FillPatch.cpp:303
amrex::Vector< amrex::MultiFab > zmom_crse_rhs
Definition: ERF.H:1274
void advance_radiation(int lev, amrex::MultiFab &cons_in, const double &dt_advance)
Advance radiation diagnostics and heating rates for one time step.
Definition: ERF_AdvanceRadiation.cpp:54
void check_for_low_temp(amrex::MultiFab &S)
Definition: ERF.cpp:3714
TurbulentPerturbation turbPert
Definition: ERF.H:1649
amrex::Vector< amrex::MultiFab > rW_old
Definition: ERF.H:1269
void check_for_negative_theta(amrex::MultiFab &S)
Definition: ERF.cpp:3749
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_w_subsid
Definition: ERF.H:1797
amrex::Vector< ERFFillPatcher > FPr_w
Definition: ERF.H:1337
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_hfx2_lev
Definition: ERF.H:1356
amrex::Vector< std::unique_ptr< amrex::MultiFab > > interval_means
Definition: ERF.H:1236
static double start_time
Definition: ERF.H:1494
int cf_width
Definition: ERF.H:1332
void compute_native_shoc_tendencies(int lev, amrex::MultiFab *cons, amrex::MultiFab *xvel, amrex::MultiFab *yvel, amrex::MultiFab *zvel, amrex::Real *w_subsid, amrex::MultiFab *tau13, amrex::MultiFab *tau23, amrex::MultiFab *hfx3, amrex::MultiFab *qfx3, amrex::MultiFab *eddyDiffs, amrex::MultiFab *z_phys_nd, const double &dt_advance)
Definition: ERF_ShocCoupling.cpp:6
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q1fx1_lev
Definition: ERF.H:1358
LandSurface lsm
Definition: ERF.H:1294
amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > phys_bc_type
Definition: ERF.H:1451
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, double dt, double time)
Definition: ERF_AdvanceDycore.cpp:40
amrex::Vector< amrex::MultiFab > rV_old
Definition: ERF.H:1267
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lat_m
Definition: ERF.H:1150
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:25
amrex::Vector< amrex::Vector< amrex::MultiFab > > vars_old
Definition: ERF.H:1226
amrex::Vector< std::unique_ptr< amrex::MultiFab > > qheating_rates
Definition: ERF.H:1305
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rad_fluxes
Definition: ERF.H:1311
static MoistureModelType modelType(const MoistureType a_moisture_type)
query if a specified moisture model is Eulerian or Lagrangian
Definition: ERF_Microphysics.H:121
void advance(int lev, int nstep, amrex::Real time, amrex::Real dt_step, const std::string &call_site, const amrex::MultiFab &cons_old, const amrex::MultiFab *z_phys_nd, const amrex::Geometry &geom, LandSurface &lsm, amrex::MultiFab *qheating, amrex::MultiFab *rad_fluxes, const amrex::MultiFab *t_surf, const amrex::MultiFab *lat_m, const amrex::MultiFab *lon_m, double epoch_time, bool have_datetime)
One radiation call at one level.
Definition: ERF_TwoStreamRadiation.cpp:297
const std::unique_ptr< amrex::EBFArrayBoxFactory > & get_const_factory() const noexcept
Return the cell-centered EB factory.
Definition: ERF_EB.H:102
@ zvel_bc
Definition: ERF_IndexDefines.H:107
@ yvel_bc
Definition: ERF_IndexDefines.H:106
@ cons_bc
Definition: ERF_IndexDefines.H:89
@ xvel_bc
Definition: ERF_IndexDefines.H:105
@ ymom
Definition: ERF_IndexDefines.H:234
@ cons
Definition: ERF_IndexDefines.H:232
@ zmom
Definition: ERF_IndexDefines.H:235
@ xmom
Definition: ERF_IndexDefines.H:233
@ ng
Definition: ERF_Morrison.H:50
@ xvel
Definition: ERF_IndexDefines.H:215
@ cons
Definition: ERF_IndexDefines.H:214
@ zvel
Definition: ERF_IndexDefines.H:217
@ yvel
Definition: ERF_IndexDefines.H:216
int qr
rain
Definition: ERF_DataStruct.H:237
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:1974
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2237
bool moisture_tight_coupling
Whether moisture updates use tight coupling.
Definition: ERF_DataStruct.H:2265
bool compute_mean_vars
Definition: ERF_DataStruct.H:2122
std::string mean_vars_reset_mode
Definition: ERF_DataStruct.H:2125
bool use_wvel_perturbation(int lev) const
Query whether vertical-velocity CPM perturbations are enabled on a level.
Definition: ERF_DataStruct.H:2168
bool use_perturbation(int lev) const
Query whether any turbulent perturbation mode is enabled on a level.
Definition: ERF_DataStruct.H:2133
bool time_avg_vel
Whether time-averaged velocity fields are output.
Definition: ERF_DataStruct.H:2119
bool use_direct_perturbation(int lev) const
Query whether direct turbulent perturbations are enabled on a level.
Definition: ERF_DataStruct.H:2146
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:1949
amrex::Real mean_vars_reset_time
Definition: ERF_DataStruct.H:2126
WindFarmType windfarm_type
Wind-farm model.
Definition: ERF_DataStruct.H:2238
bool custom_w_subsidence
Whether custom vertical subsidence is enabled.
Definition: ERF_DataStruct.H:2077
MoistureComponentIndices moisture_indices
Index map of the moisture data carried by the active scheme: conserved-state components for the speci...
Definition: ERF_DataStruct.H:2263
RadiationType rad_type
Radiation model.
Definition: ERF_DataStruct.H:2241
void calc_tpi_update(const int lev, const double dt, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel, amrex::MultiFab &mf_cons)
Update perturbation amplitudes and intervals when each box is ready.
Definition: ERF_TurbPertStruct.H:300
amrex::Vector< amrex::MultiFab > pb_cell
Per-cell perturbation amplitude storage.
Definition: ERF_TurbPertStruct.H:763
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)
Apply stored turbulent perturbations to a source or state array.
Definition: ERF_TurbPertStruct.H:409
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,
double  dt,
double  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
50 {
51  BL_PROFILE_VAR("erf_advance_dycore()",erf_advance_dycore);
52 
53  const Box& domain = fine_geom.Domain();
54 
56  cloud_chamber_budget->set_initial_state(state_old[IntVars::cons], fine_geom, 0, old_time);
57  }
58 
61 
62  MultiFab r_hse (base_state[level], make_alias, BaseState::r0_comp , 1);
63  MultiFab p_hse (base_state[level], make_alias, BaseState::p0_comp , 1);
64 
65  // These pointers are used in the MRI utility functions
66  MultiFab* r0 = &r_hse;
67  MultiFab* p0 = &p_hse;
68 
69  MultiFab* rhotheta_src_ptr = solverChoice.custom_rhotheta_forcing ? rhotheta_src[level].get() : nullptr;
70  MultiFab* rhoqt_src_ptr = solverChoice.custom_moisture_forcing ? rhoqt_src[level].get() : nullptr;
71  Real* dptr_wbar_sub = solverChoice.custom_w_subsidence ? d_w_subsid[level].data() : nullptr;
72 
73  // Turbulent Perturbation Pointer
74  //Real* dptr_rhotheta_src = solverChoice.pert_type ? d_rhotheta_src[level].data() : nullptr;
75 
76  Vector<Real*> d_rayleigh_ptrs_at_lev;
77  d_rayleigh_ptrs_at_lev.resize(Rayleigh::nvars);
78  d_rayleigh_ptrs_at_lev[Rayleigh::ubar] = solverChoice.dampingChoice.rayleigh_damp_U ? d_rayleigh_ptrs[level][Rayleigh::ubar].data() : nullptr;
79  d_rayleigh_ptrs_at_lev[Rayleigh::vbar] = solverChoice.dampingChoice.rayleigh_damp_V ? d_rayleigh_ptrs[level][Rayleigh::vbar].data() : nullptr;
80  d_rayleigh_ptrs_at_lev[Rayleigh::wbar] = solverChoice.dampingChoice.rayleigh_damp_W ? d_rayleigh_ptrs[level][Rayleigh::wbar].data() : nullptr;
81  d_rayleigh_ptrs_at_lev[Rayleigh::thetabar] = solverChoice.dampingChoice.rayleigh_damp_T ? d_rayleigh_ptrs[level][Rayleigh::thetabar].data() : nullptr;
82 
83  bool use_rayleigh =
86  Real* d_sinesq_at_lev = (use_rayleigh) ? d_sinesq_ptrs[level].data() : nullptr;
87  Real* d_sinesq_stag_at_lev = (use_rayleigh) ? d_sinesq_stag_ptrs[level].data() : nullptr;
88 
89  Vector<Real*> d_sponge_ptrs_at_lev;
90  if (SpongeChoice::sponge_type == SpongeType::Input_Sponge)
91  {
92  d_sponge_ptrs_at_lev.resize(Sponge::nvars_sponge);
93  d_sponge_ptrs_at_lev[Sponge::ubar_sponge] = d_sponge_ptrs[level][Sponge::ubar_sponge].data();
94  d_sponge_ptrs_at_lev[Sponge::vbar_sponge] = d_sponge_ptrs[level][Sponge::vbar_sponge].data();
95  }
96 
97  bool l_use_terrain_fitted_coords = (solverChoice.mesh_type != MeshType::ConstantDz);
98  bool l_use_kturb = tc.use_kturb;
99  bool l_use_diff = ( (dc.molec_diff_type != MolecDiffType::None) ||
100  l_use_kturb );
101 
102  const bool use_SurfLayer = (m_SurfaceLayer[Orientation(Direction::z, Orientation::low)] != nullptr);
103  const MultiFab* z_0 = (use_SurfLayer) ? m_SurfaceLayer[Orientation(Direction::z, Orientation::low)]->get_z0(level) : nullptr;
104 
105  const bool use_nudging = solverChoice.nudging_from_input_sounding;
106  const bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
107  const bool use_lsf = solverChoice.large_scale_forcing;
108 
109  const BoxArray& ba = state_old[IntVars::cons].boxArray();
110  const BoxArray& ba_z = zvel_old.boxArray();
111  const DistributionMapping& dm = state_old[IntVars::cons].DistributionMap();
112 
113  int num_prim = state_old[IntVars::cons].nComp() - 1;
114 
115  MultiFab S_prim (ba , dm, num_prim, state_old[IntVars::cons].nGrowVect());
116  MultiFab pi_stage (ba , dm, 1, 1);
117  MultiFab fast_coeffs(ba_z, dm, 5, 0);
118 
119  MultiFab* eddyDiffs = eddyDiffs_lev[level].get();
120  MultiFab* SmnSmn = SmnSmn_lev[level].get();
121 
122  // *****************************************************************************
123  // Planar averages for subsidence terms, nudging, and immersed forcing
124  // *****************************************************************************
125  bool use_immersed_forcing = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
126  solverChoice.buildings_type == BuildingsType::ImmersedForcing);
127 
128  Table1D<Real> dptr_r_plane, dptr_t_plane, dptr_qv_plane;
129  TableData<Real, 1> r_plane_tab, t_plane_tab, qv_plane_tab;
130 
131  Table1D<Real> dptr_u_plane, dptr_v_plane;
132  TableData<Real, 1> u_plane_tab, v_plane_tab;
133  if (use_nudging || use_immersed_forcing)
134  {
135  // Rho
136  IntVect ng_c(state_old[IntVars::cons].nGrowVect()); ng_c[2] = 1;
137  int ncomp = (solverChoice.moisture_type == MoistureType::None) ? 2 : RhoQ2_comp;
138  MultiFab cons(state_old[IntVars::cons], make_alias, 0, ncomp);
139 
140  // Immersed forcing requires z-direction (ave_plane=2) averages to match persistent table sizing
141  if (use_immersed_forcing) {
143  }
144 
145  PlaneAverage cons_ave(&cons, fine_geom, solverChoice.ave_plane, ng_c);
146  cons_ave.compute_averages(ZDir(), cons_ave.field());
147 
148  int ncell = cons_ave.ncell_line();
149 
150  Gpu::HostVector< Real> r_plane_h(ncell);
151  Gpu::DeviceVector< Real> r_plane_d(ncell);
152  Gpu::HostVector< Real> t_plane_h(ncell);
153  Gpu::DeviceVector< Real> t_plane_d(ncell);
154 
155  cons_ave.line_average(Rho_comp, r_plane_h);
156  cons_ave.line_average(RhoTheta_comp, t_plane_h);
157 
158  Gpu::copy(Gpu::hostToDevice, r_plane_h.begin(), r_plane_h.end(), r_plane_d.begin());
159  Gpu::copy(Gpu::hostToDevice, t_plane_h.begin(), t_plane_h.end(), t_plane_d.begin());
160 
161  Real* dptr_r = r_plane_d.data();
162  Real* dptr_t = t_plane_d.data();
163 
164  Box tdomain = domain; tdomain.grow(2,ng_c[2]);
165  int offset = ng_c[2];
166 
167  // For immersed forcing without nudging, write directly to persistent storage
168  // to avoid redundant allocation and kernel launch
169  if (use_immersed_forcing && !use_nudging) {
170  dptr_r_plane = r_plane_avg[level].table();
171  dptr_t_plane = t_plane_avg[level].table();
172  } else {
173  r_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
174  t_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
175  dptr_r_plane = r_plane_tab.table();
176  dptr_t_plane = t_plane_tab.table();
177  }
178 
179  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
180  {
181  dptr_r_plane(k-offset) = dptr_r[k];
182  dptr_t_plane(k-offset) = dptr_t[k];
183  });
184 
185  // U, V, and Qv velocity (only needed for nudging, not for immersed forcing)
186  if (use_nudging) {
187  if (solverChoice.moisture_type != MoistureType::None)
188  {
189  Gpu::HostVector< Real> qv_plane_h(ncell), qc_plane_h(ncell);
190  Gpu::DeviceVector<Real> qv_plane_d(ncell), qc_plane_d(ncell);
191 
192  // Water vapor
193  cons_ave.line_average(RhoQ1_comp, qv_plane_h);
194  Gpu::copy(Gpu::hostToDevice, qv_plane_h.begin(), qv_plane_h.end(), qv_plane_d.begin());
195 
196  Real* dptr_qv = qv_plane_d.data();
197  qv_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
198  dptr_qv_plane = qv_plane_tab.table();
199  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
200  {
201  dptr_qv_plane(k-offset) = dptr_qv[k];
202  });
203  }
204  // U, V velocity averages are only needed for the momentum nudging diagnostics below
205  // (either sounding-based, gated by nudging_u, or LSF-based, gated by use_lsf)
206  if (solverChoice.nudging_u || use_lsf) {
207  IntVect ng_u = xvel_old.nGrowVect(); ng_u[2] = 1;
208  IntVect ng_v = yvel_old.nGrowVect(); ng_v[2] = 1;
209 
210  PlaneAverage u_ave(&(xvel_old), fine_geom, solverChoice.ave_plane, ng_u);
211  PlaneAverage v_ave(&(yvel_old), fine_geom, solverChoice.ave_plane, ng_v);
212 
213  u_ave.compute_averages(ZDir(), u_ave.field());
214  v_ave.compute_averages(ZDir(), v_ave.field());
215 
216  int u_ncell = u_ave.ncell_line();
217  int v_ncell = v_ave.ncell_line();
218  Gpu::HostVector< Real> u_plane_h(u_ncell), v_plane_h(v_ncell);
219  Gpu::DeviceVector< Real> u_plane_d(u_ncell), v_plane_d(v_ncell);
220 
221  u_ave.line_average(0, u_plane_h);
222  v_ave.line_average(0, v_plane_h);
223 
224  Gpu::copy(Gpu::hostToDevice, u_plane_h.begin(), u_plane_h.end(), u_plane_d.begin());
225  Gpu::copy(Gpu::hostToDevice, v_plane_h.begin(), v_plane_h.end(), v_plane_d.begin());
226 
227  Real* dptr_u = u_plane_d.data();
228  Real* dptr_v = v_plane_d.data();
229 
230  Box udomain = domain; udomain.grow(2,ng_u[2]);
231  Box vdomain = domain; vdomain.grow(2,ng_v[2]);
232  u_plane_tab.resize({udomain.smallEnd(2)}, {udomain.bigEnd(2)});
233  v_plane_tab.resize({vdomain.smallEnd(2)}, {vdomain.bigEnd(2)});
234 
235  int u_offset = ng_u[2];
236  dptr_u_plane = u_plane_tab.table();
237  ParallelFor(u_ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
238  {
239  dptr_u_plane(k-u_offset) = dptr_u[k];
240  });
241 
242  int v_offset = ng_v[2];
243  dptr_v_plane = v_plane_tab.table();
244  ParallelFor(v_ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
245  {
246  dptr_v_plane(k-v_offset) = dptr_v[k];
247  });
248  }
249  }
250 
251  // Store planar averages in persistent ERF member variables for immersed forcing
252  // (only needed when nudging is also enabled; otherwise we wrote directly above)
253  if (use_immersed_forcing && use_nudging) {
254  Table1D<Real> r_avg_persistent = r_plane_avg[level].table();
255  Table1D<Real> t_avg_persistent = t_plane_avg[level].table();
256 
257  // Copy from local computation to persistent storage
258  // Both use the same Table1D indexing with offset, so direct copy
259  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
260  {
261  r_avg_persistent(k-offset) = dptr_r_plane(k-offset);
262  t_avg_persistent(k-offset) = dptr_t_plane(k-offset);
263  });
264  }
265  }
266 
267  if (use_lsf) {
268  lsf_data[level]->setVal(0.0);
269 
270  int itime_curr = 0;
271  int itime_next = 0;
272  amrex::Real coeff_curr = 1.0;
273  amrex::Real coeff_next = 0.0;
274 
275  lsf.get_forcing_time_coeffs(old_time, itime_curr, itime_next, coeff_curr, coeff_next);
276 
277  // ttend, qtend, wsub = lsf
278  // ug0, vg0 = lsf - avg u,v
279 
280  const Real* theta_lsf_n = lsf.t_int_lsf_d[itime_curr].dataPtr();
281  const Real* theta_lsf_np1 = lsf.t_int_lsf_d[itime_next].dataPtr();
282  const Real* qv_lsf_n = lsf.q_int_lsf_d[itime_curr].dataPtr();
283  const Real* qv_lsf_np1 = lsf.q_int_lsf_d[itime_next].dataPtr();
284  const Real* w_lsf_n = lsf.w_int_lsf_d[itime_curr].dataPtr();
285  const Real* w_lsf_np1 = lsf.w_int_lsf_d[itime_next].dataPtr();
286 
287 #ifdef _OPENMP
288 #pragma omp parallel if (Gpu::notInLaunchRegion())
289 #endif
290  {
291  for ( MFIter mfi(state_old[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
292  {
293  Box bx = mfi.tilebox();
294  const Array4<Real>& cell_data = state_old[IntVars::cons].array(mfi);
295  const Array4<Real>& lsf_arr = lsf_data[level]->array(mfi);
296  Real dzInv = fine_geom.InvCellSize(2);
297  const int kmin = domain.smallEnd(2) + 1; // minimum k for vertical subsidence
298  const int kmax = domain.bigEnd(2) - 1; // maximum k for vertical subsidence
299  const Array4<const Real>& z_cc_arr = (l_use_terrain_fitted_coords) ? z_phys_cc[level]->const_array(mfi) : Array4<Real>{};
300 
301  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
302  {
303  amrex::Real ttend = coeff_curr*theta_lsf_n[k] + coeff_next*theta_lsf_np1[k];
304  amrex::Real qtend = coeff_curr*qv_lsf_n[k] + coeff_next*qv_lsf_np1[k];
305  amrex::Real wsub = coeff_curr*w_lsf_n[k] + coeff_next*w_lsf_np1[k];
306 
307  lsf_arr(i, j, k, 0) = ttend;
308  lsf_arr(i, j, k, 1) = qtend;
309  lsf_arr(i, j, k, 2) = wsub;
310 
311  // horizontal tendencies
312  lsf_arr(i, j, k, 3) = ttend;
313  lsf_arr(i, j, k, 4) = qtend;
314 
315  if (k >= kmin && k <= kmax) {
316  int k1, k2;
317  amrex::Real rdz;
318  if (wsub >= 0.0)
319  {
320  k1 = k;
321  k2 = k-1;
322  } else {
323  k1 = k+1;
324  k2 = k;
325  }
326  rdz = (z_cc_arr) ? 1.0 / (z_cc_arr(i,j,k1) - z_cc_arr(i,j,k2)) : dzInv;
327  rdz *= wsub;
328 
329  amrex::Real tvtend = -rdz * ( (cell_data(i, j, k1, RhoTheta_comp) / cell_data(i, j, k1, Rho_comp)) - (cell_data(i, j, k2, RhoTheta_comp) / cell_data(i, j, k2, Rho_comp)));
330  amrex::Real qvtend = zero, qctend = zero;
331  if (has_moisture) {
332  qvtend = -rdz * ( (cell_data(i, j, k1, RhoQ1_comp) / cell_data(i, j, k1, Rho_comp)) - (cell_data(i, j, k2, RhoQ1_comp) / cell_data(i, j, k2, Rho_comp)));
333  qctend = -rdz * ( (cell_data(i, j, k1, RhoQ2_comp) / cell_data(i, j, k1, Rho_comp)) - (cell_data(i, j, k2, RhoQ2_comp) / cell_data(i, j, k2, Rho_comp)));
334  }
335 
336  lsf_arr(i, j, k, 0) += tvtend;
337  lsf_arr(i, j, k, 1) += qvtend;
338 
339  lsf_arr(i, j, k, 5) = tvtend;
340  lsf_arr(i, j, k, 6) = qvtend;
341  lsf_arr(i, j, k, 7) = qctend;
342  }
343  });
344 
345  // subsidence
346  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
347  {
348  // directly apply tendencies for theta and qv
349  cell_data(i, j, k, RhoTheta_comp) += cell_data(i, j, k, Rho_comp) * lsf_arr(i, j, k, 0) * dt_advance;
350  if (has_moisture) {
351  cell_data(i, j, k, RhoQ1_comp) = max(Real(0), cell_data(i, j, k, RhoQ1_comp) + cell_data(i, j, k, Rho_comp) * lsf_arr(i, j, k, 1) * Real(dt_advance));
352  cell_data(i, j, k, RhoQ2_comp) += cell_data(i, j, k, Rho_comp) * lsf_arr(i, j, k, 7) * dt_advance;
353  }
354  });
355  }
356  }
357  }
358 
359  if (use_nudging) {
360  nudge_data[level]->setVal(0.0);
361 
362  int itime_n = 0;
363  int itime_np1 = 0;
364  Real coeff_n = Real(1.0);
365  Real coeff_np1 = Real(0.0);
366  Real tau_inv = Real(1.0) / input_sounding_data.tau_nudging;
367 
368  int n_sounding_times = input_sounding_data.input_sounding_time.size();
369 
370  for (int nt = 1; nt < n_sounding_times; nt++) {
371  if (old_time > input_sounding_data.input_sounding_time[nt]) itime_n = nt;
372  }
373  if (itime_n == n_sounding_times-1) {
374  itime_np1 = itime_n;
375  } else {
376  itime_np1 = itime_n+1;
377  coeff_np1 = (old_time - input_sounding_data.input_sounding_time[itime_n]) /
379  coeff_n = Real(1.0) - coeff_np1;
380  }
381 
382  const Real* theta_inp_sound_n = input_sounding_data.theta_inp_sound_d[itime_n].dataPtr() + 1;
383  const Real* theta_inp_sound_np1 = input_sounding_data.theta_inp_sound_d[itime_np1].dataPtr() + 1;
384  const Real* qv_inp_sound_n = input_sounding_data.qv_inp_sound_d[itime_n].dataPtr() + 1;
385  const Real* qv_inp_sound_np1 = input_sounding_data.qv_inp_sound_d[itime_np1].dataPtr() + 1;
386 
387  const int n = RhoTheta_comp;
388  const int nq = RhoQ1_comp;
389 
390  // lower and upper bounds to apply theta nudging
391  const Real t_z1 = solverChoice.nudging_t_z1;
392  const Real t_z2 = solverChoice.nudging_t_z2;
393 
394  // whether to nudge theta at all
395  const bool nudge_theta = solverChoice.nudging_t;
396 
397  // lower and upper bounds to apply qv nudging
398  const Real q_z1 = solverChoice.nudging_q_z1;
399  const Real q_z2 = solverChoice.nudging_q_z2;
400 
401  // whether to nudge qv at all
402  const bool nudge_q = solverChoice.nudging_q;
403 
404  // lower and upper bounds to apply u,v nudging (sounding-based branch only; see below)
405  const Real u_z1 = solverChoice.nudging_u_z1;
406  const Real u_z2 = solverChoice.nudging_u_z2;
407 
408 #ifdef _OPENMP
409 #pragma omp parallel if (Gpu::notInLaunchRegion())
410 #endif
411  {
412  for ( MFIter mfi(state_old[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
413  {
414  Box bx = mfi.tilebox();
415  const Array4<Real>& cell_data = state_old[IntVars::cons].array(mfi);
416  const Array4<const Real>& z_cc_arr = (l_use_terrain_fitted_coords) ? z_phys_cc[level]->const_array(mfi) : Array4<Real>{};
417  const Array4<Real>& nudge_arr = nudge_data[level]->array(mfi);
418  Real zlo = fine_geom.ProbLo(2);
419  Real dz = fine_geom.CellSize(2);
420 
421  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
422  {
423  Real z = (z_cc_arr) ? z_cc_arr(i,j,k) : zlo + (k+0.5)*dz;
424 
425  // nudge_data = tnudge, qnudge, unudge, vnudge
426 
427  // Nudging for theta
428  if (nudge_theta && z >= t_z1 && z <= t_z2) {
429  Real nudge = (coeff_n*theta_inp_sound_n[k] + coeff_np1*theta_inp_sound_np1[k]) - (dptr_t_plane(k)/dptr_r_plane(k));
430  nudge_arr(i, j, k, 0) = nudge * tau_inv;
431  //if (i == 0 && j == 0)
432  // amrex::Print() << " nudge i = " << i << " j = " << j << " k = " << k << ": theta_n = " << theta_inp_sound_n[k] << " theta_np1 = " << theta_inp_sound_np1[k] << " t / r plane(k) = " << dptr_t_plane(k) / dptr_r_plane(k) << ": nudge = " << nudge << " nudge*tau = " << nudge*tau_inv << " gamaz = " << gamaz << std::endl;
433  cell_data(i, j, k, n) += nudge * dptr_r_plane(k) * tau_inv * dt_advance;
434  }
435 
436  // Nudging for qv
437  if (has_moisture && nudge_q && z >= q_z1 && z <= q_z2) {
438  Real nudge = (coeff_n*qv_inp_sound_n[k] + coeff_np1*qv_inp_sound_np1[k]) - (dptr_qv_plane(k)/dptr_r_plane(k));
439  nudge_arr(i, j, k, 1) = nudge * tau_inv;
440  //if (i == 0 && j == 0)
441  // amrex::Print() << " nudge i = " << i << " j = " << j << " k = " << k << " z = " << z << ": q_n = " << qv_inp_sound_n[k] << " qv_np1 = " << qv_inp_sound_np1[k] << " q / r plane(k) = " << dptr_qv_plane(k) / dptr_r_plane(k) << ": nudge = " << nudge << " nudge*tau = " << nudge*tau_inv << " dptr_qv_plane(k) = " << dptr_qv_plane(k) << " dptr_r_plane(k) = " << dptr_r_plane(k) << std::endl;
442  cell_data(i, j, k, nq) += nudge * dptr_r_plane(k) * tau_inv * dt_advance;
443  }
444  });
445 
446  // Nudging for u and v
447  // NOTE: if LSF is enabled, then the U,V nudging here uses the LSF values, not U and V from the input sounding
448  if (solverChoice.nudging_u || use_lsf) {
449  Real uv_coeff_n = coeff_n;
450  Real uv_coeff_np1 = coeff_np1;
451  Real tau = tau_inv;
452  Real* u_nudge_n, *u_nudge_np1, *v_nudge_n, *v_nudge_np1;
453  if (!use_lsf)
454  {
455  u_nudge_n = input_sounding_data.U_inp_sound_d[itime_n].dataPtr() + 1;
456  u_nudge_np1 = input_sounding_data.U_inp_sound_d[itime_np1].dataPtr() + 1;
457  v_nudge_n = input_sounding_data.V_inp_sound_d[itime_n].dataPtr() + 1;
458  v_nudge_np1 = input_sounding_data.V_inp_sound_d[itime_np1].dataPtr() + 1;
459  } else {
460  int itime_curr = 0;
461  int itime_next = 0;
462  uv_coeff_n = 1.0;
463  uv_coeff_np1 = 0.0;
464  tau = 1.0 / lsf.tau_lsf; // only applies to u,v LSF nudging
465 
466  lsf.get_forcing_time_coeffs(old_time, itime_curr, itime_next, uv_coeff_n, uv_coeff_np1);
467  u_nudge_n = lsf.u_int_lsf_d[itime_curr].dataPtr();
468  u_nudge_np1 = lsf.u_int_lsf_d[itime_next].dataPtr();
469  v_nudge_n = lsf.v_int_lsf_d[itime_curr].dataPtr();
470  v_nudge_np1 = lsf.v_int_lsf_d[itime_next].dataPtr();
471  }
472 
473  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
474  {
475  // height bound below applies only to sounding-based wind nudging, not LSF
476  Real z = (z_cc_arr) ? z_cc_arr(i,j,k) : zlo + (k+0.5)*dz;
477  if (use_lsf || (z >= u_z1 && z <= u_z2)) {
478  Real unudge = -(dptr_u_plane(k) - (uv_coeff_n*u_nudge_n[k] + uv_coeff_np1*u_nudge_np1[k]));
479  unudge *= tau;
480 
481  Real vnudge = -(dptr_v_plane(k) - (uv_coeff_n*v_nudge_n[k] + uv_coeff_np1*v_nudge_np1[k]));
482  vnudge *= tau;
483 
484  nudge_arr(i, j, k, 2) = unudge;
485  nudge_arr(i, j, k, 3) = vnudge;
486  }
487  });
488  }
489  }
490  }
491  }
492 
493  // **************************************************************************************
494  // Compute strain for use in slow RHS and Smagorinsky model
495  // **************************************************************************************
496  {
497  BL_PROFILE("erf_advance_strain");
498  if (l_use_diff) {
499 
500  const BCRec* bc_ptr_h = domain_bcs_type.data();
501  const GpuArray<Real, AMREX_SPACEDIM> dxInv = fine_geom.InvCellSizeArray();
502 
503 #ifdef _OPENMP
504 #pragma omp parallel if (Gpu::notInLaunchRegion())
505 #endif
506  for ( MFIter mfi(state_new[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
507  {
508  Box bxcc = mfi.growntilebox(IntVect(1,1,0));
509  Box tbxxy = mfi.tilebox(IntVect(1,1,0),IntVect(1,1,0));
510  Box tbxxz = mfi.tilebox(IntVect(1,0,1),IntVect(1,1,0));
511  Box tbxyz = mfi.tilebox(IntVect(0,1,1),IntVect(1,1,0));
512 
513  if (bxcc.smallEnd(2) != domain.smallEnd(2)) {
514  bxcc.growLo(2,1);
515  tbxxy.growLo(2,1);
516  tbxxz.growLo(2,1);
517  tbxyz.growLo(2,1);
518  }
519 
520  if (bxcc.bigEnd(2) != domain.bigEnd(2)) {
521  bxcc.growHi(2,1);
522  tbxxy.growHi(2,1);
523  tbxxz.growHi(2,1);
524  tbxyz.growHi(2,1);
525  }
526 
527  const Array4<const Real> & u = xvel_old.array(mfi);
528  const Array4<const Real> & v = yvel_old.array(mfi);
529  const Array4<const Real> & w = zvel_old.array(mfi);
530 
531  Array4<Real> tau11 = Tau[level][TauType::tau11].get()->array(mfi);
532  Array4<Real> tau22 = Tau[level][TauType::tau22].get()->array(mfi);
533  Array4<Real> tau33 = Tau[level][TauType::tau33].get()->array(mfi);
534  Array4<Real> tau12 = Tau[level][TauType::tau12].get()->array(mfi);
535  Array4<Real> tau13 = Tau[level][TauType::tau13].get()->array(mfi);
536  Array4<Real> tau23 = Tau[level][TauType::tau23].get()->array(mfi);
537 
538  Array4<Real> tau21 = (Tau[level][TauType::tau21]) ? Tau[level][TauType::tau21].get()->array(mfi) : Array4<Real>{};
539  Array4<Real> tau31 = (Tau[level][TauType::tau31]) ? Tau[level][TauType::tau31].get()->array(mfi) : Array4<Real>{};
540  Array4<Real> tau32 = (Tau[level][TauType::tau32]) ? Tau[level][TauType::tau32].get()->array(mfi) : Array4<Real>{};
541 
542  const Array4<const Real>& z_nd = z_phys_nd[level]->const_array(mfi);
543 
544  const Array4<const Real> mf_mx = mapfac[level][MapFacType::m_x]->const_array(mfi);
545  const Array4<const Real> mf_ux = mapfac[level][MapFacType::u_x]->const_array(mfi);
546  const Array4<const Real> mf_vx = mapfac[level][MapFacType::v_x]->const_array(mfi);
547  const Array4<const Real> mf_my = mapfac[level][MapFacType::m_y]->const_array(mfi);
548  const Array4<const Real> mf_uy = mapfac[level][MapFacType::u_y]->const_array(mfi);
549  const Array4<const Real> mf_vy = mapfac[level][MapFacType::v_y]->const_array(mfi);
550 
551  // We update Tau_corr[level] in erf_make_tau_terms, not here
552  Array4<Real> no_tau_corr_update_here{};
553 
554  if (solverChoice.mesh_type == MeshType::StretchedDz) {
555  ComputeStrain_S(bxcc, tbxxy, tbxxz, tbxyz, domain,
556  u, v, w,
557  tau11, tau22, tau33,
558  tau12, tau21,
559  tau13, tau31,
560  tau23, tau32,
561  stretched_dz_d[level], dxInv,
562  mf_mx, mf_ux, mf_vx, mf_my, mf_uy, mf_vy, bc_ptr_h,
563  no_tau_corr_update_here, no_tau_corr_update_here);
564  } else if (l_use_terrain_fitted_coords) {
565  ComputeStrain_T(bxcc, tbxxy, tbxxz, tbxyz, domain,
566  u, v, w,
567  tau11, tau22, tau33,
568  tau12, tau21,
569  tau13, tau31,
570  tau23, tau32,
571  z_nd, detJ_cc[level]->const_array(mfi), dxInv,
572  mf_mx, mf_ux, mf_vx, mf_my, mf_uy, mf_vy, bc_ptr_h,
573  no_tau_corr_update_here, no_tau_corr_update_here);
574  } else {
575  if (solverChoice.terrain_type == TerrainType::EB) {
576  ComputeStrain_EB(mfi, bxcc, tbxxy, tbxxz, tbxyz, domain,
577  u, v, w,
578  tau11, tau22, tau33,
579  tau12, tau13, tau23,
580  dxInv,
581  bc_ptr_h,
582  get_eb(level),
583  no_tau_corr_update_here, no_tau_corr_update_here);
584  } else {
585  ComputeStrain_N(bxcc, tbxxy, tbxxz, tbxyz, domain,
586  u, v, w,
587  tau11, tau22, tau33,
588  tau12, tau13, tau23,
589  dxInv,
590  mf_mx, mf_ux, mf_vx, mf_my, mf_uy, mf_vy, bc_ptr_h,
591  no_tau_corr_update_here, no_tau_corr_update_here);
592  }
593  }
594  } // mfi
595  } // l_use_diff
596  } // profile
597 
598 #include "ERF_TI_utils.H"
599 
600  // Additional SFS quantities, calculated once per timestep
601  MultiFab* Hfx1 = SFS_hfx1_lev[level].get();
602  MultiFab* Hfx2 = SFS_hfx2_lev[level].get();
603  MultiFab* Hfx3 = SFS_hfx3_lev[level].get();
604  MultiFab* Q1fx1 = SFS_q1fx1_lev[level].get();
605  MultiFab* Q1fx2 = SFS_q1fx2_lev[level].get();
606  MultiFab* Q1fx3 = SFS_q1fx3_lev[level].get();
607  MultiFab* Q2fx3 = SFS_q2fx3_lev[level].get();
608  MultiFab* Diss = SFS_diss_lev[level].get();
609 
610  MultiFab* Hfx3_EB = nullptr;
611  if (solverChoice.terrain_type == TerrainType::EB) {
612  Hfx3_EB = hfx3_EB[level].get();
613  }
614 
615  // *************************************************************************
616  // Calculate cell-centered eddy viscosity & diffusivities
617  //
618  // Notes -- we fill all the data in ghost cells before calling this so
619  // that we can fill the eddy viscosity in the ghost regions and
620  // not have to call a boundary filler on this data itself
621  //
622  // LES - updates both horizontal and vertical eddy viscosity components
623  // PBL - only updates vertical eddy viscosity components so horizontal
624  // components come from the LES model or are left as zero.
625  // *************************************************************************
626  if (l_use_kturb)
627  {
628  // NOTE: state_new transfers to state_old for PBL (due to ptr swap in advance)
629  bool l_use_moisture = ( solverChoice.moisture_type != MoistureType::None );
630  const BCRec* bc_ptr_h = domain_bcs_type.data();
631  ComputeTurbulentViscosity(dt_advance, xvel_old, yvel_old,Tau[level],
632  state_old[IntVars::cons],
633  *walldist[level].get(),
634  *eddyDiffs, *Hfx1, *Hfx2, *Hfx3, *Diss, // to be updated
635  fine_geom, mapfac[level],
636  z_phys_nd[level], z_phys_cc[level], solverChoice,
637  m_SurfaceLayer[Orientation(Direction::z, Orientation::low)], z_0, l_use_terrain_fitted_coords,
638  l_use_moisture, level,
639  bc_ptr_h,
640  get_eb(level),
641  false, // vert_only
642  qheating_rates[level].get(),
643  (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
644  solverChoice.buildings_type == BuildingsType::ImmersedForcing) ?
645  terrain_blanking[level].get() : nullptr);
646 
647  // Zero turbulent mixing in fully immersed cells
648  MultiFab* terrain_blank = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
649  solverChoice.buildings_type == BuildingsType::ImmersedForcing) ?
650  terrain_blanking[level].get() : nullptr;
651 
652  if (terrain_blank) {
653  for (MFIter mfi(*eddyDiffs); mfi.isValid(); ++mfi) {
654  const Box& bx = mfi.growntilebox(eddyDiffs->nGrowVect());
655  auto const& t_blank_arr = terrain_blank->const_array(mfi);
656  auto const& eddy_arr = eddyDiffs->array(mfi);
657 
658  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
659  // Zero eddy diffusivities in fully immersed cells (sanitizes NaN/Inf)
660  if (t_blank_arr(i,j,k) == one) {
661  for (int n = 0; n < EddyDiff::NumDiffs; ++n) {
662  eddy_arr(i,j,k,n) = zero;
663  }
664  }
665  });
666  }
667  }
668  }
669 
670  // ***********************************************************************************************
671  // Update user-defined source terms -- these are defined once per time step (not per RK stage)
672  // ***********************************************************************************************
674  prob->update_rhotheta_sources(old_time,
675  rhotheta_src_ptr,
676  fine_geom, z_phys_cc[level]);
677  }
678 
680  prob->update_rhoqt_sources(old_time,
681  rhoqt_src_ptr,
682  fine_geom, z_phys_cc[level]);
683  }
684 
686  prob->update_geostrophic_profile(old_time,
687  h_u_geos[level], d_u_geos[level],
688  h_v_geos[level], d_v_geos[level],
689  fine_geom, z_phys_cc[level]);
690  }
691 
693  prob->update_w_subsidence(old_time,
694  h_w_subsid[level], d_w_subsid[level],base_state[level],
695  fine_geom, z_phys_nd[level]);
696  }
697 
698  // ***********************************************************************************************
699  // Convert old velocity available on faces to old momentum on faces to be used in time integration
700  // ***********************************************************************************************
701  MultiFab density(state_old[IntVars::cons], make_alias, Rho_comp, 1);
702 
703  //
704  // This is an optimization since we won't need more than one ghost
705  // cell of momentum in the integrator if not using numerical diffusion
706  //
707  IntVect ngu = (!solverChoice.use_num_diff) ? IntVect(1,1,1) : xvel_old.nGrowVect();
708  IntVect ngv = (!solverChoice.use_num_diff) ? IntVect(1,1,1) : yvel_old.nGrowVect();
709  IntVect ngw = (!solverChoice.use_num_diff) ? IntVect(1,1,0) : zvel_old.nGrowVect();
710 
711  const MultiFab* c_vfrac = nullptr;
712  if (solverChoice.terrain_type == TerrainType::EB) {
713  c_vfrac = &((get_eb(level).get_const_factory())->getVolFrac());
714  }
715 
716  VelocityToMomentum(xvel_old, ngu, yvel_old, ngv, zvel_old, ngw, density,
717  state_old[IntVars::xmom],
718  state_old[IntVars::ymom],
719  state_old[IntVars::zmom],
720  domain, domain_bcs_type, c_vfrac);
721 
722  MultiFab::Copy(xvel_new,xvel_old,0,0,1,xvel_old.nGrowVect());
723  MultiFab::Copy(yvel_new,yvel_old,0,0,1,yvel_old.nGrowVect());
724  MultiFab::Copy(zvel_new,zvel_old,0,0,1,zvel_old.nGrowVect());
725 
726  bool fast_only = false;
727  bool vel_and_mom_synced = true;
728 
729  apply_bcs(state_old, old_time,
730  state_old[IntVars::cons].nGrow(), state_old[IntVars::xmom].nGrow(),
731  fast_only, vel_and_mom_synced);
732 
733  cons_to_prim(state_old[IntVars::cons], S_prim, state_old[IntVars::cons].nGrow());
734 
735  make_pi_stage(state_old[IntVars::cons]);
736 
737  // ***********************************************************************************************
738  // Define a new MultiFab that holds q_total and fill it by summing the moisture components --
739  // to be used in buoyancy calculation and as part of the inertial weighting in the
740  // ***********************************************************************************************
741 
742  const bool l_eb_terrain = (solverChoice.terrain_type == TerrainType::EB);
743  MultiFab qt(grids[level], dmap[level], 1, (l_eb_terrain) ? 2 : 1);
744  qt.setVal(0);
745 
746 #include "ERF_TI_no_substep_fun.H"
747 #include "ERF_TI_substep_fun.H"
748 #include "ERF_TI_slow_rhs_pre.H"
749 #include "ERF_TI_slow_rhs_post.H"
750 
751  // ***************************************************************************************
752  // Setup the integrator and integrate for a single timestep
753  // **************************************************************************************
754  MRISplitIntegrator<Vector<MultiFab> >& mri_integrator = *mri_integrator_mem[level];
755 
756  // Define rhs and 'post update' utility function that is called after calculating
757  // any state data (e.g. at RK stages or at the end of a timestep)
758  mri_integrator.set_slow_rhs_pre(slow_rhs_fun_pre);
759  mri_integrator.set_slow_rhs_post(slow_rhs_fun_post);
760 
763  mri_integrator.set_no_substep(no_substep_fun);
764 
765  mri_integrator.advance(state_old, state_new, old_time, dt_advance);
766 
767  if (verbose) Print() << "Done with advance_dycore at level " << level << std::endl;
768 }
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:31
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:35
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:40
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:41
void ComputeTurbulentViscosity(double 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 std::unique_ptr< MultiFab > &z_phys_cc, 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, const MultiFab *qheating_rates, const MultiFab *terrain_blank)
Definition: ERF_ComputeTurbulentViscosity.cpp:818
@ tau12
Definition: ERF_DataStruct.H:40
@ tau33
Definition: ERF_DataStruct.H:40
@ tau22
Definition: ERF_DataStruct.H:40
@ tau11
Definition: ERF_DataStruct.H:40
@ tau32
Definition: ERF_DataStruct.H:40
@ tau31
Definition: ERF_DataStruct.H:40
@ tau21
Definition: ERF_DataStruct.H:40
@ ubar
Definition: ERF_DataStruct.H:179
@ wbar
Definition: ERF_DataStruct.H:179
@ vbar
Definition: ERF_DataStruct.H:179
@ thetabar
Definition: ERF_DataStruct.H:179
@ nvars_sponge
Definition: ERF_DataStruct.H:187
@ vbar_sponge
Definition: ERF_DataStruct.H:187
@ ubar_sponge
Definition: ERF_DataStruct.H:187
@ v_x
Definition: ERF_DataStruct.H:29
@ u_y
Definition: ERF_DataStruct.H:30
@ v_y
Definition: ERF_DataStruct.H:30
@ m_y
Definition: ERF_DataStruct.H:30
@ u_x
Definition: ERF_DataStruct.H:29
@ m_x
Definition: ERF_DataStruct.H:29
DirectionSelector< 2 > ZDir
Definition: ERF_DirectionSelector.H:55
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:46
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:32
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:13
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > mapfac
Definition: ERF.H:1405
amrex::Vector< std::unique_ptr< MRISplitIntegrator< amrex::Vector< amrex::MultiFab > > > > mri_integrator_mem
Definition: ERF.H:1241
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_sinesq_stag_ptrs
Definition: ERF.H:1828
amrex::Vector< amrex::Vector< amrex::Real > > h_w_subsid
Definition: ERF.H:1796
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc
Definition: ERF.H:1374
amrex::Vector< std::unique_ptr< amrex::MultiFab > > hfx3_EB
Definition: ERF.H:1364
amrex::Vector< amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > > d_sponge_ptrs
Definition: ERF.H:1824
amrex::Vector< long > dt_mri_ratio
Definition: ERF.H:1220
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_q2fx3_lev
Definition: ERF.H:1359
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking
Definition: ERF.H:1396
amrex::Vector< amrex::TableData< amrex::Real, 1 > > t_plane_avg
Definition: ERF.H:1168
std::unique_ptr< ProblemBase > prob
Definition: ERF.H:1191
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > stretched_dz_d
Definition: ERF.H:1411
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SFS_diss_lev
Definition: ERF.H:1357
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_sinesq_ptrs
Definition: ERF.H:1827
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_v_geos
Definition: ERF.H:1803
LargeScaleForcingData lsf
Definition: ERF.H:1300
amrex::Vector< amrex::Vector< amrex::Real > > h_v_geos
Definition: ERF.H:1802
InputSoundingData input_sounding_data
Definition: ERF.H:1155
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rhotheta_src
Definition: ERF.H:1793
amrex::Vector< amrex::Vector< amrex::Real > > h_u_geos
Definition: ERF.H:1799
amrex::Vector< amrex::TableData< amrex::Real, 1 > > r_plane_avg
Definition: ERF.H:1167
amrex::Vector< std::unique_ptr< amrex::MultiFab > > SmnSmn_lev
Definition: ERF.H:1343
amrex::Vector< std::unique_ptr< amrex::MultiFab > > rhoqt_src
Definition: ERF.H:1794
amrex::Vector< std::unique_ptr< amrex::MultiFab > > nudge_data
Definition: ERF.H:1301
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > d_u_geos
Definition: ERF.H:1800
amrex::Vector< std::unique_ptr< amrex::MultiFab > > lsf_data
Definition: ERF.H:1302
static int fixed_mri_dt_ratio
Definition: ERF.H:1518
amrex::Vector< amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > > d_rayleigh_ptrs
Definition: ERF.H:1823
std::unique_ptr< CloudChamberBudget > cloud_chamber_budget
Definition: ERF.H:1277
Split integrator for MRI simulations handling slow and fast timescales.
Definition: ERF_MRI.H:20
void set_slow_rhs_post(std::function< void(T &, T &, T &, T &, const double, const double, const double, const int)> F)
Definition: ERF_MRI.H:184
void set_slow_rhs_pre(std::function< void(T &, T &, T &, const double, const double, const double, const int)> F)
Set the pre-substepping slow RHS function.
Definition: ERF_MRI.H:180
void set_no_substep(std::function< void(T &, T &, T &, const double, const double, int)> F)
Set the function to be called when acoustic substepping is disabled.
Definition: ERF_MRI.H:223
double advance(T &S_old, T &S_new, double time, const double time_step)
Advance the state from time to time + time_step.
Definition: ERF_MRI.H:241
void set_acoustic_substepping(std::function< void(int, int, int, T &, const T &, T &, T &, const double, const double, const amrex::Real, const double, const double)> F)
Set the acoustic substepping function.
Definition: ERF_MRI.H:193
void set_slow_fast_timestep_ratio(const int timestep_ratio=1)
Set the ratio of slow to fast timestep sizes.
Definition: ERF_MRI.H:205
Definition: ERF_PlaneAverage.H:14
@ p0_comp
Definition: ERF_IndexDefines.H:77
@ r0_comp
Definition: ERF_IndexDefines.H:76
@ NumDiffs
Definition: ERF_IndexDefines.H:269
@ qt
Definition: ERF_Kessler.H:30
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
real(c_double), parameter p0
Definition: ERF_module_model_constants.F90:40
real(c_double), private k1
Definition: ERF_module_mp_morr_two_moment.F90:213
real(kind=kind_phys), parameter, private r0
Definition: ERF_module_mp_wdm6.F90:75
bool rayleigh_damp_U
Whether Rayleigh damping is applied to x-momentum.
Definition: ERF_DampingStruct.H:94
bool rayleigh_damp_V
Whether Rayleigh damping is applied to y-momentum.
Definition: ERF_DampingStruct.H:95
bool rayleigh_damp_W
Whether Rayleigh damping is applied to vertical momentum.
Definition: ERF_DampingStruct.H:96
bool rayleigh_damp_T
Whether Rayleigh damping is applied to potential temperature.
Definition: ERF_DampingStruct.H:97
Definition: ERF_DiffStruct.H:22
MolecDiffType molec_diff_type
Selected molecular transport model.
Definition: ERF_DiffStruct.H:94
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > qv_inp_sound_d
Device water vapor mixing ratio profiles [kg/kg].
Definition: ERF_InputSoundingData.H:509
amrex::Vector< amrex::Real > input_sounding_time
Physical time associated with each input sounding file.
Definition: ERF_InputSoundingData.H:496
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > V_inp_sound_d
Device y velocity profiles.
Definition: ERF_InputSoundingData.H:509
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > theta_inp_sound_d
Device potential temperature profiles [K].
Definition: ERF_InputSoundingData.H:509
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > U_inp_sound_d
Device x velocity profiles.
Definition: ERF_InputSoundingData.H:509
amrex::Real tau_nudging
Nudging time scale for relaxing toward input sounding data.
Definition: ERF_InputSoundingData.H:493
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > q_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > v_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > t_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
void get_forcing_time_coeffs(const amrex::Real &time, int &curr, int &next, amrex::Real &coeff_curr, amrex::Real &coeff_next)
Determine interpolation coefficients to apply tendencies for the given time.
Definition: ERF_LargeScaleForcingData.H:329
amrex::Real tau_lsf
Definition: ERF_LargeScaleForcingData.H:401
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > u_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > w_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
bool custom_geostrophic_profile
Whether a custom geostrophic wind profile is used.
Definition: ERF_DataStruct.H:2080
bool nudging_t
Definition: ERF_DataStruct.H:2097
int ave_plane
Averaging plane index used by diagnostics.
Definition: ERF_DataStruct.H:2260
bool nudging_q
Definition: ERF_DataStruct.H:2098
amrex::Real nudging_q_z2
Definition: ERF_DataStruct.H:2094
amrex::Real nudging_u_z2
Definition: ERF_DataStruct.H:2090
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:1958
DampingChoice dampingChoice
Damping-related options.
Definition: ERF_DataStruct.H:1972
amrex::Real nudging_u_z1
Definition: ERF_DataStruct.H:2089
amrex::Real nudging_q_z1
Definition: ERF_DataStruct.H:2093
bool large_scale_forcing
Definition: ERF_DataStruct.H:2100
static BuildingsType buildings_type
Building representation.
Definition: ERF_DataStruct.H:1952
bool custom_rhotheta_forcing
Whether custom rho-theta forcing is enabled.
Definition: ERF_DataStruct.H:2075
amrex::Real nudging_t_z2
Definition: ERF_DataStruct.H:2092
DiffChoice diffChoice
Diffusion-related options.
Definition: ERF_DataStruct.H:1971
bool custom_moisture_forcing
Whether custom moisture forcing is enabled.
Definition: ERF_DataStruct.H:2076
amrex::Real nudging_t_z1
Definition: ERF_DataStruct.H:2091
bool nudging_u
Definition: ERF_DataStruct.H:2096
bool nudging_from_input_sounding
Whether solution fields are nudged toward input sounding data.
Definition: ERF_DataStruct.H:2087
bool use_num_diff
Whether sixth-order numerical diffusion is enabled.
Definition: ERF_DataStruct.H:2233
static SpongeType sponge_type
Selected sponge damping model.
Definition: ERF_SpongeStruct.H:100
Definition: ERF_TurbStruct.H:115
bool use_kturb
Whether any turbulence model is active.
Definition: ERF_TurbStruct.H:835
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 double &  time,
const double &  dt_advance 
)
11 {
12  if (solverChoice.lsm_type != LandSurfaceType::None) {
13  if (solverChoice.lsm_type == LandSurfaceType::NOAHMP) {
14  // Typed surface-precip accumulation sources from the active microphysics
15  // scheme: borrowed const views of the scheme-native cumulative accumulators
16  // plus their native->kg/m^2 (== water-equivalent mm) conversion factors.
17  // Noah-MP forms the water-equivalent interval precip (RAINBL / MP_RAINNC /
18  // MP_SNOW / MP_GRAUP / SR) from these, so schemes with differing accumulator
19  // semantics or units (e.g. SAM's density-scaled snow/graupel) are handled
20  // correctly. Empty when moisture/precip is off -> land model runs precip-free.
21  const SurfacePrecipAccumulationSources precip_sources =
22  micro ? micro->Get_Surface_Precip_Accumulation_Ptrs(lev)
24  lsm.Advance(lev, cons_in, xvel_in, yvel_in,
25  SFS_hfx3_lev[lev].get(), SFS_q1fx3_lev[lev].get(),
26  precip_sources,
27  time, dt_advance, istep[0], lsm.Get_LSM_Update_Status(0));
28  } else {
29  lsm.Advance(lev, dt_advance);
30  }
31  }
32 }
std::unique_ptr< Microphysics > micro
Definition: ERF.H:1276
amrex::Vector< int > istep
Definition: ERF.H:1197
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 SurfacePrecipAccumulationSources &precip_sources, const double &time_d, const double &dt_advance_d, const int &nstep, const bool updated_lev0)
Definition: ERF_LandSurface.H:62
bool Get_LSM_Update_Status(const int &lev)
Definition: ERF_LandSurface.H:189
LandSurfaceType lsm_type
Land-surface model.
Definition: ERF_DataStruct.H:2240
Definition: ERF_SurfacePrecipitation.H:34
Here is the call graph for this function:

◆ advance_microphysics()

void ERF::advance_microphysics ( int  lev,
amrex::MultiFab &  cons_in,
const double &  dt_advance,
const int &  iteration,
const double &  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  const auto micro_state_before = cloud_chamber_budget ?
24  cloud_chamber_budget->state_integrals(cons, geom[lev]) :
25  std::array<Real, CloudChamberBudget::NumScalars>{};
26  const amrex::MultiFab* base = solverChoice.anelastic[lev] ? &base_state[lev] : nullptr;
27  micro->Update_Micro_Vars_Lev(lev, cons, base);
28  micro->Advance(lev, static_cast<Real>(dt_advance), iteration,
29  static_cast<Real>(time), solverChoice, vars_new, z_phys_nd, phys_bc_type);
30  micro->Update_State_Vars_Lev(lev, cons, *z_phys_nd[lev]);
32  cloud_chamber_budget->record_internal_source(
33  micro_state_before, cloud_chamber_budget->state_integrals(cons, geom[lev]));
34  }
35 
36  // Sync cons[lev-1] covered cells with the moist state just written
37  // to cons[lev]. Without this, the next sub-cycle's FillPatchFineLevel
38  // for level lev would pull stale (latent-heat-less) values from
39  // cons[lev-1]'s coarse cells via cell-conservative interpolation,
40  // causing an artificial outward heat/q flux across the lev/(lev-1)
41  // boundary that drains the bubble interior.
42  if (lev > 0 && solverChoice.coupling_type == CouplingType::TwoWay &&
43  Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
44  AverageDownMoistStateTo(lev - 1);
45  }
46  }
47 }
void AverageDownMoistStateTo(int crse_lev)
Conservation-preserving fine-to-coarse average of RhoTheta and the active moist conserved components ...
Definition: ERF_AverageDown.cpp:243
CouplingType coupling_type
Multilevel coupling strategy.
Definition: ERF_DataStruct.H:2236
amrex::Vector< int > anelastic
Per-level flag selecting anelastic dynamics.
Definition: ERF_DataStruct.H:1981
Here is the call graph for this function:

◆ advance_radiation()

void ERF::advance_radiation ( int  lev,
amrex::MultiFab &  cons_in,
const double &  dt_advance 
)

Advance radiation diagnostics and heating rates for one time step.

Temporal semantics

This function is called exactly once per ERF::Advance() invocation, after the SurfaceLayer and LSM updates and before the dycore slow and fast substeps. It operates on the old state (t^n) at the beginning of the slow step.

  • RRTMGP / Simple path (erf.radiation_model = RRTMGP or Simple): a full spectral model with its own time-centering and source-term semantics. Produces qheating_rates[lev].
  • Two-stream path (erf.radiation_model = TwoStream): a shortwave and longwave model that computes heating rates from the old-state atmosphere (t^n) with clear-sky and cloudy column algorithms. The heating rates go into qheating_rates[lev], a 2-component MultiFab holding shortwave and longwave.

Source-term application

The computed qheating_rates are injected into the RhoTheta source term in ERF_MakeSources.cpp only while the slow RHS is being built (is_slow_step is true), which ensures:

  1. Radiation tendencies are applied once per slow step, not per substep.
  2. The tendencies represent the old-state atmosphere throughout all fast substeps of the current slow step.
  3. There is no temporal aliasing from repeated calls to advance_radiation() within a slow step, since there is only one call per slow step.

Key contracts

  • Radiation heating is an old-state forcing. The qheating_rates computed here are the radiative heating of the old-state atmosphere (t^n), applied as a source term while the slow RHS is built. That gives one radiative increment per slow step, consistent with the old state across every fast substep. Radiation does not adapt to the state within a slow step.
  • The two radiation paths are mutually exclusive. RRTMGP and two-stream never both run in one simulation; the if/else below selects one. Both produce qheating_rates in the same 2-component (SW, LW) format, and the source-term gate in ERF_MakeSources.cpp tests both, so exactly one matches in any given simulation.
Parameters
[in]levLevel of refinement (coarsest level is 0)
[in,out]consConservative quantities (Rho, RhoTheta, RhoQ*, RhoRE)
[in]dt_advanceTime step for this slow-step stage [seconds]
57 {
58  BL_PROFILE("ERF::advance_radiation()");
59 
60  // Fill this level's radiation fields by interpolation from its parent.
61  //
62  // Two situations need this and need exactly the same work done:
63  //
64  // * a nested patch -- a fine level that does not span the full column, for which
65  // RRTMGP cannot be run at all because it needs complete atmospheric columns; and
66  // * the first step of a level built by interp_atmos_from_coarse, whose atmospheric
67  // state came from FillCoarsePatch and is not yet thermodynamically consistent.
68  //
69  // NOTE: the coarse MultiFabs are handed to InterpFromCoarseLevel directly, with no
70  // intermediate ghosted temporary. That is only correct because each of them
71  // carries -- and has filled -- the ghost cells this overload reads. It builds a
72  // PhysBCFunctUseCoarseGhost, whose constructor derives the coarse halo the
73  // interpolation stencil needs and then asserts
74  //
75  // cghost = min(cmf.nGrowVect(), src_ghost);
76  // AMREX_ALWAYS_ASSERT(cghost.allGE(src_ghost_outside_domain));
77  //
78  // and the ParallelCopy that fills the coarse patch runs with send_ghost = cghost,
79  // i.e. it reads the coarse source's GHOST region, not only its valid region. A
80  // coarse source with too few ghost cells therefore aborts on that assert, and one
81  // whose halo was never filled silently feeds garbage to the interpolation. This
82  // is why qheating_rates and rad_fluxes are both defined with a (1,1,1) halo in
83  // ERF_MakeNewArrays.cpp, zeroed there, and FillBoundary'd below.
84  auto interp_rad_from_coarse = [&] ()
85  {
86  // Ensure the parent level's ghost cells are filled before interpolation. This is
87  // needed even when radiation did not run this step, especially with two-way coupling
88  // where the grid structure may have changed. A nested patch is exempt: it never
89  // runs radiation itself, so its halo is already whatever its own pass through this
90  // lambda interpolated into it.
91  if (!rad[lev-1]->is_nested_patch()) {
92  qheating_rates[lev-1]->FillBoundary(geom[lev-1].periodicity());
93  if (rad_fluxes[lev-1]) {
94  // The whole halo, as for qheating_rates -- the z ghosts matter too, since
95  // grids split in z (amr.no_box_split_dir = -1) put interior z interfaces in
96  // them -- but with z forced non-periodic. The z-ghost at khi+1 holds the
97  // top-of-atmosphere interface, physical data rather than a halo (see below);
98  // it lies outside the domain, so no ordinary exchange reaches it, but a
99  // z-periodic one would overwrite it with the bottom of the column.
100  const IntVect& per = geom[lev-1].periodicity().intVect();
101  rad_fluxes[lev-1]->FillBoundary(Periodicity(IntVect(per[0],per[1],0)));
102  }
103  }
104 
105  InterpFromCoarseLevel(*qheating_rates[lev], qheating_rates[lev]->nGrowVect(),
106  IntVect(0,0,0),
107  *qheating_rates[lev-1], 0, 0, 2,
108  geom[lev-1], geom[lev],
109  refRatio(lev-1), &cell_cons_interp,
111 
112  // Radiation fluxes (needed for plotfiles and diagnostics)
113  if (rad_fluxes[lev] && rad_fluxes[lev-1]) {
114  const int nc = rad_fluxes[lev]->nComp();
115 
116  InterpFromCoarseLevel(*rad_fluxes[lev], rad_fluxes[lev]->nGrowVect(),
117  IntVect(0,0,0),
118  *rad_fluxes[lev-1], 0, 0, nc,
119  geom[lev-1], geom[lev],
120  refRatio(lev-1), &cell_cons_interp,
122 
123  // The interpolation above cannot carry the top-of-atmosphere interface.
124  // RRTMGP's layout puts the TOA fluxes in the z-ghost cell at k = khi+1 (see
125  // ERF_MakeNewArrays.cpp), which is physical data rather than a halo. On the
126  // coarse side that cell does now fall inside the ghost region the ParallelCopy
127  // reads, so it serves as the stencil's neighbor above the top layer -- the right
128  // value for that role, being the next interface up in the same sequence -- but
129  // it is only ever read there. On the fine side, a level that spans the full
130  // column has its TOA outside the fine domain, and the call above is asked for no
131  // ghost cells outside the domain, so nothing is written there at all. A nested
132  // patch does not reach the model top, so its top plane is an ordinary interior
133  // interface that the call above has already filled.
134  //
135  // Move both planes into valid index space -- a single layer at the coarse domain
136  // top -- so the ordinary machinery can interpolate them horizontally, then put
137  // the result back in the fine level's ghost cell.
138  if (!rad[lev]->is_nested_patch()) {
139  const int khi_c = geom[lev-1].Domain().bigEnd(2);
140  const int khi_f = geom[lev ].Domain().bigEnd(2);
141 
142  // Flatten a level's grids onto the single layer at k = khi_c, keeping only
143  // the boxes that reach the top of their own domain. A box that stops short
144  // of it carries no TOA plane at all: flattening it would put a duplicate box
145  // in the BoxArray, for which FillBoundary is ill-defined, and would ask the
146  // copy below to read a z index outside the fab. Every box spans the column
147  // under the default amr.no_box_split_dir = 2, so this filter keeps all of
148  // them there; it matters only for amr.no_box_split_dir = -1. idx_out maps a
149  // slab box back to the box it came from, since dropping boxes breaks the
150  // one-to-one correspondence an MFIter would otherwise rely on.
151  auto top_slab = [&] (const MultiFab& mf_in, int khi_in, BoxArray& ba_out,
152  DistributionMapping& dm_out, Vector<int>& idx_out)
153  {
154  const BoxArray& ba_in = mf_in.boxArray();
155  const DistributionMapping& dm_in = mf_in.DistributionMap();
156  BoxList bl;
157  Vector<int> pmap;
158  for (int i = 0, n = int(ba_in.size()); i < n; ++i) {
159  if (ba_in[i].bigEnd(2) != khi_in) { continue; }
160  Box b = ba_in[i];
161  b.setSmall(2, khi_c); b.setBig(2, khi_c);
162  bl.push_back(b);
163  pmap.push_back(dm_in[i]);
164  idx_out.push_back(i);
165  }
166  ba_out = BoxArray(std::move(bl));
167  dm_out = DistributionMapping(std::move(pmap));
168  };
169 
170  BoxArray ba_toa_c, ba_toa_f;
171  DistributionMapping dm_toa_c, dm_toa_f;
172  Vector<int> idx_toa_c, idx_toa_f;
173  top_slab(*rad_fluxes[lev-1], khi_c, ba_toa_c, dm_toa_c, idx_toa_c);
174  top_slab(*rad_fluxes[lev ], khi_f, ba_toa_f, dm_toa_f, idx_toa_f);
175 
176  // toa_crse is the coarse source of an InterpFromCoarseLevel, so it must carry
177  // the halo that stencil reads -- (1,1,0), the z ratio being one -- and that
178  // halo must be defined. Unlike rad_fluxes it is freshly allocated here and
179  // the loop below writes valid boxes only, so zero it first: FillBoundary can
180  // define only the ghosts backed by another box's valid data, and the ones
181  // outside the domain are left to the setVal.
182  MultiFab toa_crse(ba_toa_c, dm_toa_c, nc, IntVect(1,1,0));
183  MultiFab toa_fine(ba_toa_f, dm_toa_f, nc, 0);
184  toa_crse.setVal(Real(0.0));
185 
186  for (MFIter mfi(toa_crse); mfi.isValid(); ++mfi) {
187  const Box& dbx = mfi.validbox();
188  Box sbx(dbx); sbx.shift(2, 1); // the coarse TOA, one above the top layer
189  toa_crse[mfi].template copy<RunOn::Device>(
190  (*rad_fluxes[lev-1])[idx_toa_c[mfi.index()]], sbx, 0, dbx, 0, nc);
191  }
192  // Unconditional, unlike the exchange at the top of this lambda: toa_crse is a
193  // new MultiFab every step, so its halo is undefined here whatever the parent
194  // level is -- being a nested patch says nothing about it.
195  toa_crse.FillBoundary(geom[lev-1].periodicity());
196 
197  // Both planes live at the same z index, so the ratio in z is one and the
198  // interpolation is purely horizontal.
199  IntVect rr2d(refRatio(lev-1)[0], refRatio(lev-1)[1], 1);
200  InterpFromCoarseLevel(toa_fine, IntVect(0,0,0),
201  IntVect(0,0,0),
202  toa_crse, 0, 0, nc,
203  geom[lev-1], geom[lev],
204  rr2d, &cell_cons_interp,
206 
207  for (MFIter mfi(toa_fine); mfi.isValid(); ++mfi) {
208  const Box& sbx = mfi.validbox();
209  Box dbx(sbx); dbx.shift(2, khi_f + 1 - khi_c);
210  (*rad_fluxes[lev])[idx_toa_f[mfi.index()]].template copy<RunOn::Device>(
211  toa_fine[mfi], sbx, 0, dbx, 0, nc);
212  }
213  }
214  }
215 
216  // LSM radiation output fields (surface fluxes needed by NoahMP)
217  if (solverChoice.lsm_type != LandSurfaceType::None) {
218  Vector<std::string> lsm_output_names = rad[lev]->get_lsm_output_varnames();
219 
220  for (int i = 0; i < lsm_output_names.size(); ++i) {
221  int varIdx_fine = lsm.Get_DataIdx(lev , lsm_output_names[i]);
222  int varIdx_coarse = lsm.Get_DataIdx(lev-1, lsm_output_names[i]);
223  if (varIdx_fine >= 0 && varIdx_coarse >= 0) {
224  MultiFab* lsm_fine = lsm.Get_Data_Ptr(lev , varIdx_fine);
225  MultiFab* lsm_coarse = lsm.Get_Data_Ptr(lev-1, varIdx_coarse);
226  if (lsm_fine && lsm_coarse && lsm_coarse->nComp() > 0) {
227  // LSM data are 2D surface fields: use 2D refinement ratio and pc_interp
228  // to avoid computing z-slopes from uninitialized ghost cells
229  IntVect rr2d(refRatio(lev-1)[0], refRatio(lev-1)[1], 1);
230  InterpFromCoarseLevel(*lsm_fine, IntVect(0,0,0),
231  IntVect(0,0,0),
232  *lsm_coarse, 0, 0, lsm_coarse->nComp(),
233  geom[lev-1], geom[lev],
234  rr2d, &pc_interp,
236  }
237  }
238  }
239  }
240  };
241 
243  BL_PROFILE_VAR("ERF::advance_radiation():RRTMGP", rrtmgp_region);
244 
245  // On the first step of a level that has just been built by interp_atmos_from_coarse,
246  // interpolate the heating rates and radiation fluxes from the parent instead of
247  // computing them. That level's atmospheric state came from FillCoarsePatch, which
248  // interpolates rho, theta and qv independently and so leaves them thermodynamically
249  // inconsistent; running RRTMGP on it produces NaNs (see MakeNewLevelFromCoarse).
250  // Interpolating also gives the LSM the radiation fields it needs for that step.
251  //
252  // The flag is set by whichever routine built the level and is cleared here as soon as
253  // it has been acted on, so exactly one step is skipped per level creation. Levels
254  // that read a full state of their own never have it set.
255  if (lev > 0 &&
256  lev < static_cast<int>(rad_interp_from_coarse_pending.size()) &&
258  amrex::Print() << "Interpolating radiation heating rates and fluxes from level " << lev-1
259  << " to level " << lev << " on the first step after that level was built\n";
260  interp_rad_from_coarse();
262  return;
263  }
264 
265 #ifdef ERF_USE_NETCDF
266  MultiFab *lat_ptr = lat_m[lev].get();
267  MultiFab *lon_ptr = lon_m[lev].get();
268 #else
269  MultiFab *lat_ptr = nullptr;
270  MultiFab *lon_ptr = nullptr;
271 #endif
272  // T surf from SurfaceLayer if we have it
273  MultiFab* t_surf = (m_SurfaceLayer[Orientation(Direction::z, Orientation::low)]) ? m_SurfaceLayer[Orientation(Direction::z, Orientation::low)]->get_t_surf(lev) : nullptr;
274 
275  // RRTMGP inputs names and pointers
276  Vector<std::string> lsm_input_names = rad[lev]->get_lsm_input_varnames();
277  Vector<MultiFab*> lsm_input_ptrs(lsm_input_names.size(),nullptr);
278  for (int i(0); i<lsm_input_ptrs.size(); ++i) {
279  int varIdx = lsm.Get_DataIdx(lev,lsm_input_names[i]);
280  if (varIdx >= 0) { lsm_input_ptrs[i] = lsm.Get_Data_Ptr(lev,varIdx); }
281  }
282 
283  // RRTMGP output names and pointers
284  Vector<std::string> lsm_output_names = rad[lev]->get_lsm_output_varnames();
285  Vector<MultiFab*> lsm_output_ptrs(lsm_output_names.size(),nullptr);
286  for (int i(0); i<lsm_output_ptrs.size(); ++i) {
287  int varIdx = lsm.Get_DataIdx(lev,lsm_output_names[i]);
288  if (varIdx >= 0) { lsm_output_ptrs[i] = lsm.Get_Data_Ptr(lev,varIdx); }
289  }
290 
291  // Force radiation update to sync with lsm?
292  bool lsm_updated = (lev==0 && max_level>0) ? lsm.Get_LSM_Update_Status(lev) : false;
293 
294  // Enter radiation class driver
295  double time_for_rad = t_old[lev] + start_time;
296  rad[lev]->Run(lev, istep[lev], time_for_rad, dt_advance,
297  cons.boxArray(), geom[lev], &(cons),
298  lmask_lev[lev][0].get(), t_surf,
299  lsm_input_ptrs, lsm_output_ptrs,
300  qheating_rates[lev].get(), rad_fluxes[lev].get(),
301  z_phys_nd[lev].get() , lat_ptr, lon_ptr,
302  lsm_updated);
303 
304  // Fill ghost cells after radiation computes (needed for interpolation to finer levels)
305  // This should be fast since it only fills this level's own ghost cells
306  if (solverChoice.rad_type != RadiationType::None && !rad[lev]->is_nested_patch()) {
307  qheating_rates[lev]->FillBoundary(geom[lev].periodicity());
308  }
309 
310  // For nested patches (fine levels that don't reach model top), radiation
311  // was skipped. Interpolate the radiation fields from the parent level.
312  if (lev > 0 && rad[lev]->is_nested_patch()) {
313  interp_rad_from_coarse();
314  }
315  }
316  // Two-stream radiation driver, a separate path from the IRadiation
317  // models above; erf.radiation_model selects exactly one of them.
318  //
319  // - The call happens exactly once per slow step (from ERF::Advance).
320  // - The heating rates computed here are old-state based (t^n).
321  // - They are injected into the RhoTheta source only on is_slow_step
322  // (see ERF_MakeSources.cpp), so there is no duplicate forcing.
323  // - istep[lev] is the CSV row index, t_old[lev] the time logged with it,
324  // and dt_advance the step size (used by the surface-energy-balance
325  // update, which runs at the post-dycore call).
326  // - The sun, the site and the surface temperature come from the same
327  // sources RRTMGP uses: start_time + t for the calendar, the lat_m/lon_m
328  // fields of a WRF or metgrid grid, and the surface layer's temperature.
329  else if (solverChoice.rad_type == RadiationType::TwoStream) {
330 #ifdef ERF_USE_NETCDF
331  const MultiFab* lat_ptr = lat_m[lev].get();
332  const MultiFab* lon_ptr = lon_m[lev].get();
333 #else
334  const MultiFab* lat_ptr = nullptr;
335  const MultiFab* lon_ptr = nullptr;
336 #endif
337  const MultiFab* t_surf = (m_SurfaceLayer[Orientation::zlo()])
338  ? m_SurfaceLayer[Orientation::zlo()]->get_t_surf(lev)
339  : nullptr;
340  two_stream_rad.advance(lev, istep[lev], t_old[lev], dt_advance, "pre_dycore",
341  vars_old[lev][Vars::cons], z_phys_nd[lev].get(), geom[lev],
342  lsm, qheating_rates[lev].get(), rad_fluxes[lev].get(),
343  t_surf, lat_ptr, lon_ptr,
344  t_old[lev] + start_time, use_datetime);
345  }
346 }
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > lmask_lev
Definition: ERF.H:1348
amrex::Vector< std::unique_ptr< IRadiation > > rad
Definition: ERF.H:1304
amrex::Vector< int > rad_interp_from_coarse_pending
Definition: ERF.H:1214
amrex::Vector< double > t_old
Definition: ERF.H:1218
int Get_DataIdx(const int &lev, std::string &varname)
Definition: ERF_LandSurface.H:152
amrex::MultiFab * Get_Data_Ptr(const int &lev, const int &varIdx)
Definition: ERF_LandSurface.H:108
@ nc
Definition: ERF_Morrison.H:46
bool rad_uses_interface() const
True for the models that run through the IRadiation interface (RRTMGP, Simple); the two-stream model ...
Definition: ERF_DataStruct.H:2245
Here is the call graph for this function:

◆ appendPlot3DVariables()

void ERF::appendPlot3DVariables ( const amrex::Vector< std::string > &  plot_var_names,
amrex::Vector< std::string > &  a_plot_var_names 
)
private
221 {
222  Vector<std::string> tmp_plot_names(0);
223 #ifdef ERF_USE_PARTICLES
224  Vector<std::string> particle_mesh_plot_names;
225  particleData.GetMeshPlotVarNames( particle_mesh_plot_names );
226  if (particle_mesh_plot_names.size() > 0) {
227  static bool first_call = true;
228  if (first_call) {
229  Print() << "ParticleData: the following additional Eulerian variables are available to plot:\n";
230  for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
231  Print() << " " << particle_mesh_plot_names[i] << "\n";
232  }
233  first_call = false;
234  }
235  for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
236  std::string tmp(particle_mesh_plot_names[i]);
237  if (containerHasElement(plot_var_names, tmp) &&
238  !containerHasElement(tmp_plot_names, tmp)) {
239  tmp_plot_names.push_back(tmp);
240  }
241  }
242  }
243 #endif
244 
245  {
246  Vector<std::string> microphysics_plot_names;
247  micro->GetPlotVarNames(microphysics_plot_names);
248  if (microphysics_plot_names.size() > 0) {
249  static bool first_call = true;
250  if (first_call) {
251  Print() << getEnumNameString(solverChoice.moisture_type)
252  << ": the following additional variables are available to plot:\n";
253  for (int i = 0; i < microphysics_plot_names.size(); i++) {
254  Print() << " " << microphysics_plot_names[i] << "\n";
255  }
256  first_call = false;
257  }
258  for (auto& plot_name : microphysics_plot_names) {
259  if (containerHasElement(plot_var_names, plot_name) &&
260  !containerHasElement(tmp_plot_names, plot_name)) {
261  tmp_plot_names.push_back(plot_name);
262  }
263  }
264  }
265  }
266 
267  for (int i = 0; i < tmp_plot_names.size(); i++) {
268  if (!containerHasElement(a_plot_var_names, tmp_plot_names[i])) {
269  a_plot_var_names.push_back(tmp_plot_names[i]);
270  }
271  }
272 }
bool containerHasElement(const V &iterable, const T &query)
Definition: ERF_Container.H:5
@ tmp
Definition: ERF_AdvanceWSM6.cpp:116
Here is the call graph for this function:

◆ appendPlotVariables()

void ERF::appendPlotVariables ( const std::string &  pp_plot_var_names,
amrex::Vector< std::string > &  plot_var_names 
)
private
184 {
185  ParmParse pp(pp_prefix);
186 
187  Vector<std::string> plot_var_names(0);
188  if (pp.contains(pp_plot_var_names.c_str())) {
189  std::string nm;
190  int nPltVars = pp.countval(pp_plot_var_names.c_str());
191  for (int i = 0; i < nPltVars; i++) {
192  pp.get(pp_plot_var_names.c_str(), nm, i);
193  // Add the named variable to our list of plot variables
194  // if it is not already in the list
195  if (!containerHasElement(plot_var_names, nm)) {
196  plot_var_names.push_back(nm);
197  }
198  }
199  }
200 
201  appendPlot3DVariables(plot_var_names, a_plot_var_names);
202 
203  // Finally, check to see if we found all the requested variables
204  for (const auto& plot_name : plot_var_names) {
205  if (!containerHasElement(a_plot_var_names, plot_name)) {
206  if (amrex::ParallelDescriptor::IOProcessor()) {
207  Warning("\nWARNING: Requested to plot variable '" + plot_name + "' but it is not available");
208  }
209  }
210  }
211 }
ParmParse pp("prob")
std::string pp_prefix
Definition: ERF.H:847
void appendPlot3DVariables(const amrex::Vector< std::string > &plot_var_names, amrex::Vector< std::string > &a_plot_var_names)
Definition: ERF_Plotfile.cpp:219
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 
)

Apply 3D isotropic/anisotropic Gaussian smoothing to cell-centered perturbations.

Parameters
levInteger specifying the current level
mf_cc_pertMultiFab containing perturbations to smooth in place
118 {
119  const Geometry& gm = geom[lev];
120  const Real dx = gm.CellSize(0);
121  const Real dy = gm.CellSize(1);
122  const Real dz = gm.CellSize(2);
123 
125 
126  const int rx = static_cast<int>(std::ceil(3.0 * sigma / dx));
127  const int ry = static_cast<int>(std::ceil(3.0 * sigma / dy));
128  const int rz = static_cast<int>(std::ceil(3.0 * sigma / dz));
129 
130  const int ncomp = mf_cc_pert.nComp();
131 
132  // 1. Build 1D Gaussian weight vectors on host
133  auto compute_1d_weights = [](int r, Real d, Real sig) {
134  Vector<Real> w(2*r + 1);
135  Real sum = 0.0;
136  for (int m = -r; m <= r; ++m) {
137  Real val = std::exp(-(m * m * d * d) / (2.0 * sig * sig));
138  w[m + r] = val;
139  sum += val;
140  }
141  for (auto& v : w) v /= sum;
142  return w;
143  };
144 
145  Vector<Real> wx_h = compute_1d_weights(rx, dx, sigma);
146  Vector<Real> wy_h = compute_1d_weights(ry, dy, sigma);
147  Vector<Real> wz_h = compute_1d_weights(rz, dz, sigma);
148 
149  Gpu::DeviceVector<Real> wx_d(wx_h.size()), wy_d(wy_h.size()), wz_d(wz_h.size());
150  Gpu::copy(Gpu::hostToDevice, wx_h.begin(), wx_h.end(), wx_d.begin());
151  Gpu::copy(Gpu::hostToDevice, wy_h.begin(), wy_h.end(), wy_d.begin());
152  Gpu::copy(Gpu::hostToDevice, wz_h.begin(), wz_h.end(), wz_d.begin());
153 
154  Real const* wx = wx_d.data();
155  Real const* wy = wy_d.data();
156  Real const* wz = wz_d.data();
157 
158  // Temp MultiFab for separable intermediate states
159  IntVect ngrow(AMREX_D_DECL(rx, ry, rz));
160  MultiFab mf_tmp(mf_cc_pert.boxArray(), mf_cc_pert.DistributionMap(), ncomp, ngrow);
161 
162  // --- Pass 1: X Direction ---
163  mf_tmp.ParallelCopy(mf_cc_pert, 0, 0, ncomp, IntVect(0), ngrow, gm.periodicity());
164  for (MFIter mfi(mf_cc_pert, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
165  const Box& bx = mfi.tilebox();
166  auto const& in = mf_tmp.const_array(mfi);
167  auto const& out = mf_cc_pert.array(mfi);
168  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept {
169  Real sum = 0.0;
170  for (int m = -rx; m <= rx; ++m) {
171  sum += wx[m + rx] * in(i + m, j, k, n);
172  }
173  out(i, j, k, n) = sum;
174  });
175  }
176 
177  // --- Pass 2: Y Direction ---
178  mf_tmp.ParallelCopy(mf_cc_pert, 0, 0, ncomp, IntVect(0), ngrow, gm.periodicity());
179  for (MFIter mfi(mf_cc_pert, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
180  const Box& bx = mfi.tilebox();
181  auto const& in = mf_tmp.const_array(mfi);
182  auto const& out = mf_cc_pert.array(mfi);
183  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept {
184  Real sum = 0.0;
185  for (int nn = -ry; nn <= ry; ++nn) {
186  sum += wy[nn + ry] * in(i, j + nn, k, n);
187  }
188  out(i, j, k, n) = sum;
189  });
190  }
191 
192  // --- Pass 3: Z Direction ---
193  mf_tmp.ParallelCopy(mf_cc_pert, 0, 0, ncomp, IntVect(0), ngrow, gm.periodicity());
194  for (MFIter mfi(mf_cc_pert, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
195  const Box& bx = mfi.tilebox();
196  auto const& in = mf_tmp.const_array(mfi);
197  auto const& out = mf_cc_pert.array(mfi);
198  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept {
199  Real sum = 0.0;
200  for (int p = -rz; p <= rz; ++p) {
201  sum += wz[p + rz] * in(i, j, k + p, n);
202  }
203  out(i, j, k, n) = sum;
204  });
205  }
206 
208 }
const Real dy
Definition: ERF_InitCustomPert_ABL.H:45
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
amrex::Real sigma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:11
void NormalizeMultiFabRMS_PerComponent(MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:64
@ nn
Definition: ERF_WDM6.H:32
@ p
Definition: ERF_WSM6.H:280
amrex::Real ens_pert_correlated_radius
Spatial correlation radius for ensemble perturbations.
Definition: ERF_DataStruct.H:2325
Here is the call graph for this function:

◆ ApplyOceanSurfaceState()

void ERF::ApplyOceanSurfaceState ( const amrex::Vector< amrex::MultiFab * > &  state,
double  time,
const amrex::iMultiFab *  erf_coverage = nullptr 
)
833 {
834  // The gate is "is coupled SST configured"
838  amrex::Print() << "ERF::ApplyOceanSurfaceState was called but "
839  << "erf.use_coupled_sst is not set; the ocean SST is "
840  << "being discarded. Set erf.use_coupled_sst = 1."
841  << std::endl;
842  }
843  return;
844  }
845 
846  if (state.empty() || state[0] == nullptr) { return; }
847 
848  if (m_coupled_sst == nullptr) {
849  amrex::BoxArray ba;
850  amrex::DistributionMapping dm;
852  // No ghost cells: SurfaceLayer::fill_tsurf_with_coupled_sst clamps its sample
853  // index into the valid box, exactly as get_lsm_tsurf does for the LSM
854  // arrays, so ghosts here would never be read. t_surf's own ghosts are
855  // filled by the FillBoundary at the end of update_fluxes.
856  m_coupled_sst = std::make_unique<amrex::MultiFab> (ba, dm, 1, 0);
857  m_coupled_sst_valid = std::make_unique<amrex::iMultiFab>(ba, dm, 1, 0);
858  }
859 
860  // Zero coverage first: a cell the driver does not write this call must not
861  // inherit the previous call's coverage flag.
862  m_coupled_sst_valid->setVal(0);
863  m_coupled_sst->ParallelCopy(*state[0], 0, 0, 1);
864  if (erf_coverage != nullptr) {
865  m_coupled_sst_valid->ParallelCopy(*erf_coverage, 0, 0, 1);
866  }
867 
868  // Coupled SST is only consumed by the bottom z face.
869  if (m_SurfaceLayer[amrex::Orientation::zlo()]) {
870  m_SurfaceLayer[amrex::Orientation::zlo()]->update_coupled_sst_ptr(
871  0, m_coupled_sst.get(), m_coupled_sst_valid.get());
872  }
873 
874  // Report over the covered cells only. The old whole-array min/max was
875  // dominated by the remap's zero fill wherever the ocean grid did not reach,
876  // so the range check could not distinguish "cold ocean" from "no ocean".
877  constexpr amrex::Real cov_min_sentinel = amrex::Real( 1.e30);
878  constexpr amrex::Real cov_max_sentinel = amrex::Real(-1.e30);
879 
880  amrex::ReduceOps<amrex::ReduceOpSum, amrex::ReduceOpMin, amrex::ReduceOpMax> reduce_ops;
881  amrex::ReduceData<amrex::Long, amrex::Real, amrex::Real> reduce_data(reduce_ops);
882  using ReduceTuple = typename decltype(reduce_data)::Type;
883 
884  for (amrex::MFIter mfi(*m_coupled_sst); mfi.isValid(); ++mfi) {
885  const amrex::Box& bx = mfi.validbox();
886  const auto sst_arr = m_coupled_sst->const_array(mfi);
887  const auto valid_arr = m_coupled_sst_valid->const_array(mfi);
888  reduce_ops.eval(bx, reduce_data,
889  [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
890  {
891  const bool covered = (valid_arr(i,j,k) != 0);
892  return { covered ? amrex::Long(1) : amrex::Long(0),
893  covered ? sst_arr(i,j,k) : cov_min_sentinel,
894  covered ? sst_arr(i,j,k) : cov_max_sentinel };
895  });
896  }
897 
898  auto reduced = reduce_data.value(reduce_ops);
899  amrex::Long n_valid = amrex::get<0>(reduced);
900  amrex::Real cov_min = amrex::get<1>(reduced);
901  amrex::Real cov_max = amrex::get<2>(reduced);
902 
903  amrex::ParallelDescriptor::ReduceLongSum(n_valid);
904  amrex::ParallelDescriptor::ReduceRealMin(cov_min);
905  amrex::ParallelDescriptor::ReduceRealMax(cov_max);
906 
907  const amrex::Long n_total = m_coupled_sst->boxArray().numPts();
908 
909  if (n_valid == 0) {
910  amrex::Print() << "Coupled SST apply at t=" << time
911  << " s: no ERF cell has an ocean donor; the lower-boundary "
912  << "SST/TSK data stands everywhere." << std::endl;
913  return;
914  }
915 
916  amrex::Print() << "Coupled SST apply at t=" << time
917  << " s: covered " << n_valid << " of " << n_total
918  << " surface cells, min/max over covered = "
919  << cov_min << " / " << cov_max << " K" << std::endl;
920 
921  if (cov_min < amrex::Real(260.0) || cov_max > amrex::Real(320.0)) {
922  amrex::Warning("Coupled SST is outside the expected [260, 320] K range");
923  }
924 }
void GetOceanToAtmosSurfaceLayout(amrex::BoxArray &ba, amrex::DistributionMapping &dm)
Definition: ERF_to_REMORA.cpp:263
std::unique_ptr< amrex::iMultiFab > m_coupled_sst_valid
Definition: ERF.H:960
bool m_warned_coupled_sst_declined
Definition: ERF.H:963
std::unique_ptr< amrex::MultiFab > m_coupled_sst
Definition: ERF.H:959
Definition: ERF_ConsoleIO.cpp:15
bool use_coupled_sst
Definition: ERF_DataStruct.H:2250
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 }
void AverageDownTo(int crse_lev, int scomp, int ncomp, bool do_perturbational_and_momenta=true)
Definition: ERF_AverageDown.cpp:36
Here is the call graph for this function:

◆ AverageDownMoistStateTo()

void ERF::AverageDownMoistStateTo ( int  crse_lev)

Conservation-preserving fine-to-coarse average of RhoTheta and the active moist conserved components using detJ/mfac weighting, reusing AverageDownTo with perturbational-state and momentum 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:1701
@ th0_comp
Definition: ERF_IndexDefines.H:79
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
145 {
146  // Mask for zeroing covered cells
147  AMREX_ASSERT(level > 0);
148 
149  BoxArray cba = grids[level-1];
150  DistributionMapping cdm = dmap[level-1];
151 
152  BoxArray fba = grids[level];
153 
154  iMultiFab ifine_mask_lev = makeFineMask(cba, cdm, fba, ref_ratio[level-1], 1, 0);
155 
156  const auto fma = fine_mask_lev.arrays();
157  const auto ifma = ifine_mask_lev.arrays();
158  ParallelFor(fine_mask_lev, [=] AMREX_GPU_DEVICE(int bno, int i, int j, int k) noexcept
159  {
160  fma[bno](i,j,k) = ifma[bno](i,j,k);
161  });
162 }
Here is the call graph for this function:

◆ BuildPlot3DScratch()

void ERF::BuildPlot3DScratch ( const amrex::Vector< std::string > &  plot_var_names,
Plot3DScratch scratch,
int  max_lev = -1,
bool  sync_solution = true 
)
300 {
301  auto& mf_cc_vel = scratch.mf_cc_vel;
302  auto& mf_cc_tau = scratch.mf_cc_tau;
303  auto& mf_cc_fx = scratch.mf_cc_fx;
304 
305  // The scratch is indexed by level whatever is built, so that a level the
306  // caller does not read is simply an empty MultiFab rather than a shift.
307  mf_cc_vel.resize(finest_level+1);
308  mf_cc_tau.resize(finest_level+1);
309  mf_cc_fx.resize(finest_level+1);
310 
311  const int top_lev = (max_lev < 0) ? finest_level : std::min(max_lev, finest_level);
312 
313  // Lagrangian microphysics with AMR (TwoWay): synchronize the
314  // microphysics-owned storage before rebuilding the active moisture state
315  // used by the plotfile diagnostics. This modifies the coarse solution, so
316  // a caller that runs at every step rather than at plotfile cadence asks not
317  // to do it: turning a diagnostic on must not change the answer.
318  if (sync_solution
319  && Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian
320  && solverChoice.coupling_type == CouplingType::TwoWay
321  && finest_level >= 1) {
322  micro->AverageDownMicroVars(finest_level);
323  for (int flev = finest_level-1; flev >= 0; --flev) {
325  }
326  }
327 
328  // We Fillpatch here because some of the derived quantities require derivatives
329  // which require ghost cells to be filled. We do not need to call FillPatcher
330  // because we don't need to set interior fine points.
331  // NOTE: the momenta here are only used as scratch space, the momenta themselves are not fillpatched
332 
333  // Level 0 FillPatch
335  &vars_new[0][Vars::yvel], &vars_new[0][Vars::zvel]});
336 
337  for (int lev = 1; lev <= top_lev; ++lev) {
338  bool fillset = false;
339  FillPatchFineLevel(lev, t_new[lev], {&vars_new[lev][Vars::cons], &vars_new[lev][Vars::xvel],
340  &vars_new[lev][Vars::yvel], &vars_new[lev][Vars::zvel]},
341  {&vars_new[lev][Vars::cons], &rU_new[lev], &rV_new[lev], &rW_new[lev]},
342  base_state[lev], base_state[lev], fillset);
343  }
344 
345  // Get qmoist pointers if using moisture
346  for (int lev = 0; lev <= finest_level; ++lev) {
347  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
348  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
349  }
350  }
351 
352  // Array of MultiFabs for cell-centered velocity
353 
354  if (containerHasElement(plot_var_names, "x_velocity" ) ||
355  containerHasElement(plot_var_names, "y_velocity" ) ||
356  containerHasElement(plot_var_names, "z_velocity" ) ||
357  containerHasElement(plot_var_names, "magvel" ) ||
358  containerHasElement(plot_var_names, "helicity" ) ||
359  containerHasElement(plot_var_names, "local_helicity") ||
360  containerHasElement(plot_var_names, "vort_stretching") ||
361  containerHasElement(plot_var_names, "vorticity_x" ) ||
362  containerHasElement(plot_var_names, "vorticity_y" ) ||
363  containerHasElement(plot_var_names, "vorticity_z" ) ) {
364 
365  for (int lev = 0; lev <= top_lev; ++lev) {
366  mf_cc_vel[lev].define(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,1));
367  mf_cc_vel[lev].setVal(bogus_large_value);
368  average_face_to_cellcenter(mf_cc_vel[lev],0,
369  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],
370  &vars_new[lev][Vars::yvel],
371  &vars_new[lev][Vars::zvel]}, 1);
372  } // lev
373  } // if (vel or vort)
374 
375 
376  if (containerHasElement(plot_var_names, "Tau11" ) ||
377  containerHasElement(plot_var_names, "Tau12" ) ||
378  containerHasElement(plot_var_names, "Tau13" ) ||
379  containerHasElement(plot_var_names, "Tau21" ) ||
380  containerHasElement(plot_var_names, "Tau22" ) ||
381  containerHasElement(plot_var_names, "Tau23" ) ||
382  containerHasElement(plot_var_names, "Tau31" ) ||
383  containerHasElement(plot_var_names, "Tau32" ) ||
384  containerHasElement(plot_var_names, "Tau33" )) {
385 
386  for (int lev = 0; lev <= top_lev; ++lev) {
387  mf_cc_tau[lev].define(grids[lev], dmap[lev], 9, IntVect(1,1,1));
388  mf_cc_tau[lev].setVal(bogus_large_value);
389 
390 #ifdef _OPENMP
391 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
392 #endif
393  for ( MFIter mfi(mf_cc_tau[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
394  {
395  // todo - clean this up
396  const Box& bxcc= mfi.tilebox();
397 
398  const Array4<Real>& tau12 = (Tau[lev][TauType::tau12]) ? Tau[lev][TauType::tau12]->array(mfi) : Array4<Real>{};
399  const Array4<Real>& tau21 = (Tau[lev][TauType::tau21]) ? Tau[lev][TauType::tau21]->array(mfi) : Array4<Real>{};
400 
401  const Array4<Real>& tau13 = (Tau[lev][TauType::tau13]) ? Tau[lev][TauType::tau13]->array(mfi) : Array4<Real>{};
402  const Array4<Real>& tau31 = (Tau[lev][TauType::tau31]) ? Tau[lev][TauType::tau31]->array(mfi) : Array4<Real>{};
403 
404  const Array4<Real>& tau23 = (Tau[lev][TauType::tau23]) ? Tau[lev][TauType::tau23]->array(mfi) : Array4<Real>{};
405  const Array4<Real>& tau32 = (Tau[lev][TauType::tau32]) ? Tau[lev][TauType::tau32]->array(mfi) : Array4<Real>{};
406 
407  const Array4<Real>& tau_cc = mf_cc_tau[lev].array(mfi);
408  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
409  tau_cc(i, j, k, TauType::tau12) = (tau12) ? fourth * (tau12(i, j, k) + tau12(i, j+1, k) + tau12(i+1,j+1,k) + tau12(i+1, j, k)) : zero;
410  tau_cc(i, j, k, TauType::tau21) = (tau21) ? fourth * (tau21(i, j, k) + tau21(i, j+1, k) + tau21(i+1,j+1,k) + tau21(i+1, j, k)) : zero;
411  tau_cc(i, j, k, TauType::tau13) = (tau13) ? fourth * (tau13(i, j, k) + tau13(i, j, k+1) + tau13(i+1,j,k+1) + tau13(i+1, j, k)) : zero;
412  tau_cc(i, j, k, TauType::tau31) = (tau31) ? fourth * (tau31(i, j, k) + tau31(i, j, k+1) + tau31(i+1,j,k+1) + tau31(i+1, j, k)) : zero;
413  tau_cc(i, j, k, TauType::tau23) = (tau23) ? fourth * (tau23(i, j, k) + tau23(i, j, k+1) + tau23(i,j+1,k+1) + tau23(i, j+1, k)) : zero;
414  tau_cc(i, j, k, TauType::tau32) = (tau32) ? fourth * (tau32(i, j, k) + tau32(i, j, k+1) + tau32(i,j+1,k+1) + tau32(i, j+1, k)) : zero;
415  });
416  }
417  }
418  }
419 
420  if (containerHasElement(plot_var_names, "hfx1" ) ||
421  containerHasElement(plot_var_names, "hfx2" ) ||
422  containerHasElement(plot_var_names, "hfx3" ) ||
423  containerHasElement(plot_var_names, "q1fx1" ) ||
424  containerHasElement(plot_var_names, "q1fx2" ) ||
425  containerHasElement(plot_var_names, "q1fx3" ) ||
426  containerHasElement(plot_var_names, "q2fx3" ))
427  {
428  for (int lev = 0; lev <= top_lev; ++lev) {
429  mf_cc_fx[lev].define(grids[lev], dmap[lev], 7, IntVect(1,1,1));
430  mf_cc_fx[lev].setVal(bogus_large_value);
431 
432 #ifdef _OPENMP
433 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
434 #endif
435  for ( MFIter mfi(mf_cc_fx[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
436  {
437  const Box& bxcc= mfi.tilebox();
438 
439  const Array4<Real>& hfx1 = (SFS_hfx1_lev[lev]) ? SFS_hfx1_lev[lev]->array(mfi) : Array4<Real>{};
440  const Array4<Real>& hfx2 = (SFS_hfx2_lev[lev]) ? SFS_hfx2_lev[lev]->array(mfi) : Array4<Real>{};
441  const Array4<Real>& hfx3 = (SFS_hfx3_lev[lev]) ? SFS_hfx3_lev[lev]->array(mfi) : Array4<Real>{};
442  const Array4<Real>& q1fx1 = (SFS_q1fx1_lev[lev]) ? SFS_q1fx1_lev[lev]->array(mfi) : Array4<Real>{};
443  const Array4<Real>& q1fx2 = (SFS_q1fx2_lev[lev]) ? SFS_q1fx2_lev[lev]->array(mfi) : Array4<Real>{};
444  const Array4<Real>& q1fx3 = (SFS_q1fx3_lev[lev]) ? SFS_q1fx3_lev[lev]->array(mfi) : Array4<Real>{};
445  const Array4<Real>& q2fx3 = (SFS_q2fx3_lev[lev]) ? SFS_q2fx3_lev[lev]->array(mfi) : Array4<Real>{};
446 
447  const Array4<Real>& tau_fx = mf_cc_fx[lev].array(mfi);
448  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
449  tau_fx(i, j, k, 0) = (hfx1) ? 0.5 * (hfx1(i, j, k) + hfx1(i+1, j , k)) : 0.0;
450  tau_fx(i, j, k, 1) = (hfx2) ? 0.5 * (hfx2(i, j, k) + hfx2(i , j+1, k)) : 0.0;
451  tau_fx(i, j, k, 2) = (hfx3) ? 0.5 * (hfx3(i, j, k) + hfx3(i , j , k+1)) : 0.0;
452  tau_fx(i, j, k, 3) = (q1fx1) ? 0.5 * (q1fx1(i, j, k) + q1fx1(i+1, j , k)) : 0.0;
453  tau_fx(i, j, k, 4) = (q1fx2) ? 0.5 * (q1fx2(i, j, k) + q1fx2(i , j+1, k)) : 0.0;
454  tau_fx(i, j, k, 5) = (q1fx3) ? 0.5 * (q1fx3(i, j, k) + q1fx3(i , j , k+1)) : 0.0;
455  tau_fx(i, j, k, 6) = (q2fx3) ? 0.5 * (q2fx3(i, j, k) + q2fx3(i , j , k+1)) : 0.0;
456  });
457  }
458  }
459  }
460 
461  // We need ghost cells if computing vorticity
462  if ( containerHasElement(plot_var_names, "vorticity_x")||
463  containerHasElement(plot_var_names, "vorticity_y") ||
464  containerHasElement(plot_var_names, "vorticity_z") )
465  {
466  amrex::Interpolater* mapper = &cell_cons_interp;
467  FillBdyCCVels(mf_cc_vel[0],geom[0]);
468  for (int lev = 1; lev <= top_lev; ++lev)
469  {
470  Real new_time = static_cast<Real>(t_new[lev]);
471  Vector<MultiFab*> fmf = {&(mf_cc_vel[lev]), &(mf_cc_vel[lev])};
472  Vector<Real> ftime = {new_time,new_time};
473  Vector<MultiFab*> cmf = {&mf_cc_vel[lev-1], &mf_cc_vel[lev-1]};
474  Vector<Real> ctime = {new_time,new_time};
475 
476  // Call FillPatch which ASSUMES that all ghost cells at lev-1 have already been filled
477  FillPatchTwoLevels(mf_cc_vel[lev], mf_cc_vel[lev].nGrowVect(), IntVect(0,0,0),
478  static_cast<Real>(t_new[lev]), cmf, ctime, fmf, ftime,
479  0, 0, mf_cc_vel[lev].nComp(), geom[lev-1], geom[lev],
480  refRatio(lev-1), mapper, domain_bcs_type,
482  FillBdyCCVels(mf_cc_vel[lev],geom[lev]);
483  } // lev
484  } // if (vort)
485 }
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:35
void FillBdyCCVels(amrex::MultiFab &mf_cc_vel, amrex::Geometry &lev_geom)
Definition: ERF_FillBdyCCVels.cpp:17
amrex::Vector< amrex::Vector< amrex::MultiFab * > > qmoist
Definition: ERF.H:1279
@ rho0_bc_comp
Definition: ERF_IndexDefines.H:116
Here is the call graph for this function:

◆ canonicalizePlot3DVariables()

void ERF::canonicalizePlot3DVariables ( amrex::Vector< std::string > &  plot_var_names)
private
58 {
59  // Get state variables in the same order as we define them,
60  // since they may be in any order in the input list.
61  Vector<std::string> tmp_plot_names;
63  // This is called before vars_new is allocated, so the microphysics interface
64  // is the source of truth for the conserved-state layout.
65  //
66  // NOTE: the allocated width bounds the dry names and the non-water species
67  // that sit above the moist window, but *not* the moist components
68  // themselves: those are selected from solverChoice.moisture_indices,
69  // because a scheme may allocate moist components it never integrates
70  // (see the MoistureComponentIndices class comment) and those must not
71  // reach a plotfile.
74  micro->Get_Qstate_Moist_Size(),
75  micro->Get_Qstate_Size());
78  // qsrc_sw / qsrc_lw are available whenever qheating_rates is allocated,
79  // i.e. for any erf.radiation_model other than None.
80  capabilities.radiation_heating_storage =
82  capabilities.eddy_diffusivity_storage = true;
83  capabilities.dissipation_storage = true;
84  capabilities.wall_distance_storage = true;
85  for (int lev = 0; lev <= max_level; ++lev) {
86  capabilities.eddy_diffusivity_storage =
87  capabilities.eddy_diffusivity_storage && solverChoice.turbChoice[lev].use_kturb;
88  capabilities.dissipation_storage =
89  capabilities.dissipation_storage &&
91  solverChoice.turbChoice[lev].use_kturb);
92  capabilities.wall_distance_storage =
93  capabilities.wall_distance_storage &&
94  solverChoice.turbChoice[lev].rans_type != RANSType::None;
95  }
96 
97  for (int i = 0; i < cons_names.size(); ++i) {
98  if (containerHasElement(plot_var_names, cons_names[i])) {
100  tmp_plot_names.push_back(cons_names[i]);
101  }
102  }
103  }
104 
105  // check for velocity since it's not in cons_names
106  // if we are asked for any velocity component, we will need them all
107  if (containerHasElement(plot_var_names, "x_velocity") ||
108  containerHasElement(plot_var_names, "y_velocity") ||
109  containerHasElement(plot_var_names, "z_velocity")) {
110  tmp_plot_names.push_back("x_velocity");
111  tmp_plot_names.push_back("y_velocity");
112  tmp_plot_names.push_back("z_velocity");
113  }
114 
115  //
116  // If the model we are running doesn't have the variable listed in the inputs file,
117  // just ignore it rather than aborting
118  //
119 #ifdef ERF_USE_WINDFARM
120  // NOTE: these mirror the conditions guarding the Nturb/SMark fills in Write3DPlotFile;
121  // if they diverge, the names and the filled components will not line up.
122  const bool wf_is_AD = (solverChoice.windfarm_type == WindFarmType::SimpleAD ||
123  solverChoice.windfarm_type == WindFarmType::GeneralAD);
124  const bool wf_active = (solverChoice.windfarm_type == WindFarmType::Fitch ||
125  solverChoice.windfarm_type == WindFarmType::EWP || wf_is_AD);
126 #endif
127 
128  for (int i = 0; i < derived_names.size(); ++i) {
129  if ( containerHasElement(plot_var_names, derived_names[i]) ) {
130  bool ok_to_add = ( (solverChoice.terrain_type == TerrainType::ImmersedForcing || solverChoice.buildings_type == BuildingsType::ImmersedForcing ) ||
131  (derived_names[i] != "terrain_IB_mask") );
132  ok_to_add &= ( ibseb_params.enable ||
133  (derived_names[i] != "ibseb_nfaces" && derived_names[i] != "ibseb_tskin" &&
134  derived_names[i] != "ibseb_sw_abs" && derived_names[i] != "ibseb_shadow" &&
135  derived_names[i] != "ibseb_lw_net" && derived_names[i] != "ibseb_f_sky" &&
136  derived_names[i] != "ibseb_H" && derived_names[i] != "ibseb_G") );
137  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
138  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
139  (derived_names[i] != "detJ") );
140  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
141  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
142  (derived_names[i] != "z_phys") );
143  ok_to_add &= ( (SolverChoice::terrain_type == TerrainType::StaticFittedMesh) ||
144  (SolverChoice::terrain_type == TerrainType::MovingFittedMesh) ||
145  (derived_names[i] != "h_xi" && derived_names[i] != "h_eta" &&
146  derived_names[i] != "h_zeta") );
147 #ifdef ERF_USE_WINDFARM
148  // NOTE: the windfarm names must be added here, in derived_names order, since
149  // that is where Write3DPlotFile fills them (see ERF.H "MUST MATCH THE ORDER").
150  ok_to_add &= ( wf_active ||
151  (derived_names[i] != "num_turb" && derived_names[i] != "SMark0") );
152  ok_to_add &= ( wf_is_AD || (derived_names[i] != "SMark1") );
153 #endif
154  if (ok_to_add)
155  {
157  tmp_plot_names.push_back(derived_names[i]);
158  }
159  } // use_terrain?
160  } // hasElement
161  }
162 
163 #ifdef ERF_USE_PARTICLES
164  Vector<std::string> configured_particle_names;
165  for (const auto& name : particleData.getNamesUnalloc()) {
166  configured_particle_names.push_back(name);
167  }
168  for (const auto& name : particleData.getNames()) {
169  if (!containerHasElement(configured_particle_names, name)) {
170  configured_particle_names.push_back(name);
171  }
172  }
174  plot_var_names, configured_particle_names)) {
175  tmp_plot_names.push_back(name + "_count");
176  }
177 #endif
178 
179  plot_var_names = tmp_plot_names;
180 }
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:101
IBSEBParams ibseb_params
Definition: ERF.H:1392
const amrex::Vector< std::string > derived_names
Definition: ERF.H:1576
const amrex::Vector< std::string > cons_names
Definition: ERF.H:1567
void plot3d_set_state_capabilities(Plot3DSelectionCapabilities &caps, const MoistureComponentIndices &mi, const int qstate_moist_size, const int qstate_size)
Record the conserved-state layout of a run on its capabilities.
Definition: ERF_PlotfileSelection.H:84
bool plot3d_fixed_variable_available(const std::string &name, const Plot3DSelectionCapabilities &caps)
Definition: ERF_PlotfileSelection.H:211
amrex::Vector< std::string > plot3d_selected_particle_count_names(const amrex::Vector< std::string > &requested, const amrex::Vector< std::string > &configured)
Definition: ERF_PlotfileSelection.H:280
bool radiation_heating_storage_available(RadiationType rad_model)
Whether the SW/LW heating-rate storage (qsrc_sw, qsrc_lw) exists: every erf.radiation_model other tha...
Definition: ERF_PlotfileSelection.H:22
bool enable
Master switch.
Definition: ERF_IBSEBParams.H:26
What the active run can supply to a 3D plotfile.
Definition: ERF_PlotfileSelection.H:39
bool eddy_diffusivity_storage
Definition: ERF_PlotfileSelection.H:64
bool dissipation_storage
Definition: ERF_PlotfileSelection.H:65
bool radiation_heating_storage
Definition: ERF_PlotfileSelection.H:63
bool wall_distance_storage
Definition: ERF_PlotfileSelection.H:66
bool time_average_storage
Definition: ERF_PlotfileSelection.H:61
bool interval_mean_storage
Definition: ERF_PlotfileSelection.H:62
Here is the call graph for this function:

◆ check_for_low_temp()

void ERF::check_for_low_temp ( amrex::MultiFab &  S)
3715 {
3716  // *****************************************************************************
3717  // Test for low temp (low is defined as beyond the microphysics range of validity)
3718  // *****************************************************************************
3719  //
3720  // This value is defined in erf_dtesati in Source/Utils/ERF_MicrophysicsUtils.H
3721  Real t_low = Real(273.16) - Real(85.);
3722  //
3723  for (MFIter mfi(S); mfi.isValid(); ++mfi)
3724  {
3725  Box bx = mfi.tilebox();
3726  const Array4<Real> &s_arr = S.array(mfi);
3727  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
3728  {
3729  const Real rho = s_arr(i, j, k, Rho_comp);
3730  const Real rhotheta = s_arr(i, j, k, RhoTheta_comp);
3731  const Real qv = s_arr(i, j, k, RhoQ1_comp) / rho;
3732 
3733  Real temp = getTgivenRandRTh(rho, rhotheta, qv);
3734 
3735  if (temp < t_low) {
3736 #ifdef AMREX_USE_GPU
3737  AMREX_DEVICE_PRINTF("Temperature too low in cell: %d %d %d %e \n", i,j,k,temp);
3738 #else
3739  printf("Temperature too low in cell: %d %d %d \n", i,j,k);
3740  printf("Based on temp / rhotheta / rho / qv %e %e %e %e \n", temp,rhotheta,rho,qv);
3741 #endif
3742  Abort();
3743  }
3744  });
3745  }
3746 }
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_Kessler.H:25
@ qv
Definition: ERF_Kessler.H:31
constexpr int S
Definition: ERF_TwoStreamColumn.H:604
Here is the call graph for this function:

◆ check_for_negative_theta()

void ERF::check_for_negative_theta ( amrex::MultiFab &  S)
3750 {
3751  // *****************************************************************************
3752  // Test for negative (rho theta)
3753  // *****************************************************************************
3754  for (MFIter mfi(S); mfi.isValid(); ++mfi)
3755  {
3756  Box bx = mfi.tilebox();
3757  const Array4<Real> &s_arr = S.array(mfi);
3758  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
3759  {
3760  const Real rho = s_arr(i, j, k, Rho_comp);
3761  const Real rhotheta = s_arr(i, j, k, RhoTheta_comp);
3762 
3763  if (rho <= zero) {
3764 #ifdef AMREX_USE_GPU
3765  AMREX_DEVICE_PRINTF("Rho is negative at %d %d %d %e \n", i,j,k,rho);
3766 #else
3767  printf("Rho is negative at %d %d %d %e \n", i,j,k,rho);
3768  Abort("Bad rho in check_for_negative_theta");
3769 #endif
3770  }
3771 
3772  if (rhotheta <= zero) {
3773 #ifdef AMREX_USE_GPU
3774  AMREX_DEVICE_PRINTF("RhoTheta is negative at %d %d %d %e \n", i,j,k,rhotheta);
3775 #else
3776  printf("RhoTheta is negative at %d %d %d %e \n", i,j,k,rhotheta);
3777  Abort("Bad theta in check_for_negative_theta");
3778 #endif
3779  }
3780 
3781  });
3782  } // mfi
3783 }
Here is the call graph for this function:

◆ check_mesh_type()

void ERF::check_mesh_type ( int  lev)
3788 {
3789  if (SolverChoice::mesh_type == MeshType::VariableDz) {
3790  MultiFab z_slab(convert(ba2d[lev],IntVect(1,1,1)),dmap[lev],1,0);
3791  int klo = geom[lev].Domain().smallEnd(2);
3792  for (MFIter mfi(z_slab); mfi.isValid(); ++mfi) {
3793  Box nbx = mfi.tilebox();
3794  Array4<Real const> const& z_arr = z_phys_nd[lev]->const_array(mfi);
3795  Array4<Real > const& z_slab_arr = z_slab.array(mfi);
3796  ParallelFor(nbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
3797  {
3798  z_slab_arr(i,j,k) = z_arr(i,j,klo);
3799  });
3800  }
3801  Real z_min = z_slab.min(0);
3802  Real z_max = z_slab.max(0);
3803 
3804  auto dz = geom[lev].CellSize()[2];
3805  if (z_max - z_min < Real(1.e-8) * dz) {
3806  SolverChoice::set_mesh_type(MeshType::StretchedDz);
3807  if (verbose > 0) {
3808  amrex::Print() << "Resetting mesh type to StretchedDz since terrain is flat" << std::endl;
3809  }
3810  }
3811  }
3812 }
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
amrex::Vector< amrex::BoxArray > ba2d
Definition: ERF.H:1737
static void set_mesh_type(MeshType new_mesh_type)
Override the globally selected mesh type.
Definition: ERF_DataStruct.H:1965
Here is the call graph for this function:

◆ check_state_for_nans()

void ERF::check_state_for_nans ( amrex::MultiFab const &  S)
3621 {
3622  amrex::Gpu::DeviceScalar<int> d_found(0);
3623 
3624  // comp, i, j, k
3625  amrex::Gpu::DeviceVector<int> d_info(4, -1);
3626 
3627  for (MFIter mfi(S,TilingIfNotGPU()); mfi.isValid(); ++mfi)
3628  {
3629  const Box& bx = mfi.tilebox();
3630  auto const& s_arr = S.const_array(mfi);
3631 
3632  const int ncomp = S.nComp();
3633 
3634  int* found = d_found.dataPtr();
3635  int* info = d_info.dataPtr();
3636 
3637  ParallelFor(bx,
3638  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
3639  {
3640  // Somebody already found a NaN
3641  if (*found) return;
3642 
3643  for (int n = 0; n < ncomp; ++n)
3644  {
3645  Real val = s_arr(i,j,k,n);
3646 
3647  if (val != val) // NaN test
3648  {
3649  // Only one thread wins
3650  if (amrex::Gpu::Atomic::CAS(found,0,1) == 0)
3651  {
3652  info[0] = n;
3653  info[1] = i;
3654  info[2] = j;
3655  info[3] = k;
3656  }
3657  return;
3658  }
3659  }
3660  });
3661  }
3662 
3663  amrex::Gpu::streamSynchronize();
3664 
3665  if (d_found.dataValue())
3666  {
3667  amrex::Vector<int> h_info(4);
3668  amrex::Print() << "Found flag = " << d_found.dataValue() << "\n";
3669 
3670  amrex::Gpu::copy(
3671  amrex::Gpu::deviceToHost,
3672  d_info.begin(),
3673  d_info.end(),
3674  h_info.begin());
3675 
3676  std::cout << "NaN found in component " << h_info[0]
3677  << " at (i,j,k) = ("
3678  << h_info[1] << ", "
3679  << h_info[2] << ", "
3680  << h_info[3] << ")\n";
3681 
3682  amrex::Abort("NaN detected in state");
3683  }
3684 }
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 
)
3688 {
3689  //
3690  // Test at the end of every full timestep whether the solution data contains NaNs
3691  //
3692  bool any_have_nans = false;
3693  if (xvel.contains_nan(0,1,0))
3694  {
3695  amrex::Print() << "x-velocity contains NaNs " << '\n';
3696  any_have_nans = true;
3697  }
3698  if (yvel.contains_nan(0,1,0))
3699  {
3700  amrex::Print() << "y-velocity contains NaNs" << '\n';
3701  any_have_nans = true;
3702  }
3703  if (zvel.contains_nan(0,1,0))
3704  {
3705  amrex::Print() << "z-velocity contains NaNs" << '\n';
3706  any_have_nans = true;
3707  }
3708  if (any_have_nans) {
3709  exit(0);
3710  }
3711 }

◆ ClearLevel()

void ERF::ClearLevel ( int  lev)
override
1180 {
1181  for (int var_idx = 0; var_idx < Vars::NumTypes; ++var_idx) {
1182  vars_new[lev][var_idx].clear();
1183  vars_old[lev][var_idx].clear();
1184  }
1185 
1186  base_state[lev].clear();
1187 
1188  rU_new[lev].clear();
1189  rU_old[lev].clear();
1190  rV_new[lev].clear();
1191  rV_old[lev].clear();
1192  rW_new[lev].clear();
1193  rW_old[lev].clear();
1194 
1195  if (lev > 0) {
1196  zmom_crse_rhs[lev].clear();
1197  }
1198 
1199  if ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) {
1200  pp_inc[lev].clear();
1201  }
1202  if (solverChoice.anelastic[lev] == 0) {
1203  lagged_delta_rt[lev].clear();
1204  }
1205  avg_xmom[lev].clear();
1206  avg_ymom[lev].clear();
1207  avg_zmom[lev].clear();
1208 
1209  // Clears the integrator memory
1210  mri_integrator_mem[lev].reset();
1211 
1212  // Clears the map of the vertical extent of the grid column over each (i,j)
1213  column_kextent[lev].reset();
1214 
1215  // Clears the physical boundary condition routines
1216  physbcs_cons[lev].reset();
1217  physbcs_u[lev].reset();
1218  physbcs_v[lev].reset();
1219  physbcs_w[lev].reset();
1220  physbcs_base[lev].reset();
1221 
1222  // Clears the flux register array (only allocated for TwoWay coupling)
1223  if (advflux_reg[lev]) {
1224  advflux_reg[lev]->reset();
1225  }
1226 
1227  // Clears the 2D arrays
1228  if (sst_lev[lev][0]) {
1229  for (int n = 0; n < sst_lev[lev].size(); n++) {
1230  sst_lev[lev][n].reset();
1231  }
1232  }
1233  if (tsk_lev[lev][0]) {
1234  for (int n = 0; n < tsk_lev[lev].size(); n++) {
1235  tsk_lev[lev][n].reset();
1236  }
1237  }
1238  if (lat_m[lev]) {
1239  lat_m[lev].reset();
1240  }
1241  if (lon_m[lev]) {
1242  lon_m[lev].reset();
1243  }
1244  if (sinPhi_m[lev]) {
1245  sinPhi_m[lev].reset();
1246  }
1247  if (cosPhi_m[lev]) {
1248  cosPhi_m[lev].reset();
1249  }
1250 
1251 #ifdef ERF_USE_FFT
1252  // Clear any FFT solvers built at this level
1253  if (m_3D_poisson.size() > lev) {
1254  for (int n = 0; n < m_3D_poisson[lev].size(); n++) {
1255  m_3D_poisson[lev][n].reset();
1256  }
1257  }
1258  if (m_2D_poisson.size() > lev) {
1259  for (int n = 0; n < m_2D_poisson[lev].size(); n++) {
1260  m_2D_poisson[lev][n].reset();
1261  }
1262  }
1263 #endif
1264 
1265  // This level is going away, so nothing is pending for its radiation. Whichever routine
1266  // builds the level next is responsible for setting this again.
1267  if (lev < static_cast<int>(rad_interp_from_coarse_pending.size())) {
1269  }
1270 }
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > column_kextent
Definition: ERF.H:1747
amrex::Vector< amrex::MultiFab > avg_xmom
Definition: ERF.H:1248
amrex::Vector< amrex::MultiFab > pp_inc
Definition: ERF.H:1244
amrex::Vector< amrex::MultiFab > lagged_delta_rt
Definition: ERF.H:1247
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > sst_lev
Definition: ERF.H:1346
amrex::Vector< amrex::YAFluxRegister * > advflux_reg
Definition: ERF.H:1424
amrex::Vector< std::unique_ptr< amrex::MultiFab > > sinPhi_m
Definition: ERF.H:1152
amrex::Vector< std::unique_ptr< amrex::MultiFab > > cosPhi_m
Definition: ERF.H:1152
amrex::Vector< amrex::MultiFab > avg_ymom
Definition: ERF.H:1249
amrex::Vector< std::unique_ptr< ERFPhysBCFunct_base > > physbcs_base
Definition: ERF.H:1257
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > tsk_lev
Definition: ERF.H:1347
amrex::Vector< amrex::MultiFab > avg_zmom
Definition: ERF.H:1250
@ NumTypes
Definition: ERF_IndexDefines.H:218
amrex::Vector< int > project_initial_velocity
Per-level flag for projecting the initial velocity.
Definition: ERF_DataStruct.H:1984

◆ cloud_fraction()

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

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::MultiFab const &  mf_mx,
amrex::MultiFab const &  mf_my,
amrex::MultiFab const &  mf_vx,
amrex::MultiFab const &  mf_uy,
amrex::MultiFab const &  ax_in,
amrex::MultiFab const &  ay_in,
amrex::MultiFab const &  dJ_in,
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

Parameters
levLevel index for the projection data
rhsCell-centered divergence field to be filled
rho0_u_constFace-centered momentum components to differentiate
geom_at_levGeometry for the level being projected
25 {
26  BL_PROFILE("ERF::compute_divergence()");
27 
28  auto dxInv = geom_at_lev.InvCellSizeArray();
29 
30  // ****************************************************************************
31  // Compute divergence which will form RHS
32  // Note that we replace "rho0w" with the contravariant momentum, Omega
33  // ****************************************************************************
34  if (solverChoice.terrain_type == TerrainType::EB)
35  {
36  bool already_on_centroids = true;
37  EB_computeDivergence(rhs, rho0_u_const, geom_at_lev, already_on_centroids);
38  }
39  else if (SolverChoice::mesh_type == MeshType::ConstantDz)
40  {
41  computeDivergence(rhs, rho0_u_const, geom_at_lev);
42  }
43  else
44  {
45  for ( MFIter mfi(rhs,TilingIfNotGPU()); mfi.isValid(); ++mfi)
46  {
47  Box bx = mfi.tilebox();
48  const Array4<Real const>& rho0u_arr = rho0_u_const[0]->const_array(mfi);
49  const Array4<Real const>& rho0v_arr = rho0_u_const[1]->const_array(mfi);
50  const Array4<Real const>& rho0w_arr = rho0_u_const[2]->const_array(mfi);
51  const Array4<Real >& rhs_arr = rhs.array(mfi);
52 
53  const Array4<Real const>& mf_mx_arr = mf_mx.const_array(mfi);
54  const Array4<Real const>& mf_my_arr = mf_my.const_array(mfi);
55  const Array4<Real const>& mf_vx_arr = mf_vx.const_array(mfi);
56  const Array4<Real const>& mf_uy_arr = mf_uy.const_array(mfi);
57 
58  if (SolverChoice::mesh_type == MeshType::StretchedDz)
59  {
60  Real* stretched_dz_d_ptr = stretched_dz_d[lev].data();
61  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
62  {
63  Real inv_dz = one/stretched_dz_d_ptr[k];
64  Real mfsq = mf_mx_arr(i,j,0) * mf_my_arr(i,j,0);
65  rhs_arr(i,j,k) = ( (rho0u_arr(i+1,j ,k )/mf_uy_arr(i+1,j,0) - rho0u_arr(i,j,k)/mf_uy_arr(i,j,0)) * dxInv[0]
66  +(rho0v_arr(i ,j+1,k )/mf_vx_arr(i,j+1,0) - rho0v_arr(i,j,k)/mf_vx_arr(i,j,0)) * dxInv[1]
67  +(rho0w_arr(i ,j ,k+1)/mfsq - rho0w_arr(i,j,k)/mfsq ) * inv_dz ) * mfsq;
68  });
69  }
70  else
71  {
72  //
73  // Note we compute the divergence using "rho0w" == Omega
74  //
75  const Array4<Real const>& ax_arr = ax_in.const_array(mfi);
76  const Array4<Real const>& ay_arr = ay_in.const_array(mfi);
77  const Array4<Real const>& dJ_arr = dJ_in.const_array(mfi);
78 
79  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
80  {
81  Real mfsq = mf_mx_arr(i,j,0) * mf_my_arr(i,j,0);
82  rhs_arr(i,j,k) = ( ( ax_arr(i+1,j,k)*rho0u_arr(i+1,j,k)/mf_uy_arr(i+1,j,0)
83  -ax_arr(i ,j,k)*rho0u_arr(i ,j,k)/mf_uy_arr(i ,j,0) ) * dxInv[0]
84  + ( ay_arr(i,j+1,k)*rho0v_arr(i,j+1,k)/mf_vx_arr(i,j+1,0)
85  -ay_arr(i,j ,k)*rho0v_arr(i,j ,k)/mf_vx_arr(i,j ,0) ) * dxInv[1]
86  +( rho0w_arr(i,j,k+1)/mfsq
87  - rho0w_arr(i,j,k )/mfsq ) * dxInv[2] ) * mfsq / dJ_arr(i,j,k);
88  });
89  }
90  } // mfi
91  }
92 }
Here is the call graph for this function:

◆ compute_max_buoyancy_gradp_diagnostic()

void ERF::compute_max_buoyancy_gradp_diagnostic ( int  lev)
308 {
309  // When anelastic the pressure gradient comes from the projection, not from
310  // a perturbational pressure, so this diagnostic doesn't apply
311  if (solverChoice.anelastic[lev]) return;
312 
313  auto& lev_new = vars_new[lev];
314 
315  const BoxArray& ba = lev_new[Vars::cons].boxArray();
316  const DistributionMapping& dm = lev_new[Vars::cons].DistributionMap();
317 
318  // *******************************************************************************
319  // Compute the gradient of the perturbational pressure exactly as the dycore does
320  // *******************************************************************************
321 
322  Vector<MultiFab> lgradp; lgradp.resize(AMREX_SPACEDIM);
323  lgradp[GpVars::gpx].define(lev_new[Vars::xvel].boxArray(), lev_new[Vars::xvel].DistributionMap(), 1, 0);
324  lgradp[GpVars::gpx].setVal(0.);
325  lgradp[GpVars::gpy].define(lev_new[Vars::yvel].boxArray(), lev_new[Vars::yvel].DistributionMap(), 1, 0);
326  lgradp[GpVars::gpy].setVal(0.);
327  lgradp[GpVars::gpz].define(lev_new[Vars::zvel].boxArray(), lev_new[Vars::zvel].DistributionMap(), 1, 0);
328  lgradp[GpVars::gpz].setVal(0.);
329 
330  MultiFab p0(base_state[lev], make_alias, BaseState::p0_comp, 1);
331 
332  make_gradp_pert(lev, solverChoice, geom[lev], lev_new, p0,
333  *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
334  get_eb(lev), lgradp);
335 
336  // *******************************************************************************
337  // Compute the buoyancy term exactly as the dycore does
338  // *******************************************************************************
339 
340  MultiFab qt(ba, dm, 1, 1);
341  qt.setVal(0.);
342  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
343  if (solverChoice.moisture_type != MoistureType::None) {
344  make_qt(lev_new[Vars::cons], qt, n_qstate_into_total);
345  }
346 
347  //
348  // NOTE: we must fill one ghost cell of S_prim here because make_buoyancy
349  // reads cell_prim(i,j,k-1) at the lower z face of every box
350  //
351  MultiFab S_prim(ba, dm, lev_new[Vars::cons].nComp()-1, 1);
352  cons_to_prim(lev_new[Vars::cons], S_prim, 1);
353 
354  // Initialize to zero because buoyancy is not defined on the faces at the top and bottom of the domain
355  MultiFab buoyancy(lgradp[GpVars::gpz].boxArray(), lgradp[GpVars::gpz].DistributionMap(), 1, 0);
356  buoyancy.setVal(0.);
357 
358  make_buoyancy(lev, lev_new, S_prim, qt, buoyancy, geom[lev], solverChoice, base_state[lev],
359  n_qstate_into_total, get_eb(lev), solverChoice.anelastic[lev]);
360 
361  // *******************************************************************************
362  // Now form the combined term that appears in the z-momentum equation:
363  // -d(p - p0)/dz + buoyancy
364  // If the initial state is in HSE and the buoyancy is computed from (rho - rho0)
365  // (i.e. buoyancy_type = 1) then this is identically zero
366  // *******************************************************************************
367 
368  MultiFab combined(lgradp[GpVars::gpz].boxArray(), lgradp[GpVars::gpz].DistributionMap(), 1, 0);
369  combined.setVal(0.);
370 
371  MultiFab::Copy (combined, buoyancy , 0, 0, 1, 0);
372  MultiFab::Subtract(combined, lgradp[GpVars::gpz], 0, 0, 1, 0);
373 
374  // Don't include the top and bottom faces of the domain, where neither term is defined
375  Box zface_domain = surroundingNodes(geom[lev].Domain(), 2);
376  int klo = zface_domain.smallEnd(2);
377  int khi = zface_domain.bigEnd(2);
378  zface_domain.growLo(2,-1);
379  zface_domain.growHi(2,-1);
380 
381  int comp = 0;
382  Real min_val = combined.min(zface_domain,comp);
383  Real max_val = combined.max(zface_domain,comp);
384 
385  Print() << " " << std::endl;
386  if (max_val != zero || min_val != zero) {
387  IntVect min_loc = combined.minIndex(comp);
388  IntVect max_loc = combined.maxIndex(comp);
389  Print() << "Min/max value of -dp'/dz + buoyancy are " << min_val << " " << max_val;
390  if (min_loc[2] != klo && min_loc[2] != khi) Print() << " with min at face " << min_loc;
391  if (max_loc[2] != klo && max_loc[2] != khi) Print() << " with max at face " << max_loc;
392  Print() << std::endl;
393  } else {
394  Print() << "Min/max value of -dp'/dz + buoyancy are zero " << std::endl;
395  }
396  Print() << " " << std::endl;
397 }
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
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
void make_gradp_pert(int level, const SolverChoice &solverChoice, const Geometry &geom, Vector< MultiFab > &S_data, const MultiFab &p0, const MultiFab &z_phys_nd, const MultiFab &z_phys_cc, Vector< std::unique_ptr< MultiFab >> &mapfac, const eb_ &ebfact, Vector< MultiFab > &gradp)
Definition: ERF_MakeGradP.cpp:28
void make_qt(const MultiFab &cons_state, MultiFab &qt, int n_qstate_into_total)
Definition: ERF_Utils.cpp:108
@ gpz
Definition: ERF_IndexDefines.H:226
@ gpy
Definition: ERF_IndexDefines.H:225
@ gpx
Definition: ERF_IndexDefines.H:224
Here is the call graph for this function:

◆ compute_max_pressure_gradient_diagnostic()

void ERF::compute_max_pressure_gradient_diagnostic ( int  lev)
15 {
16  // We don't require HSE when anelastic because the pressure gradient
17  // is computed from the Poisson solve
18  if (solverChoice.anelastic[lev]) return;
19 
20  auto& lev_new = vars_new[lev];
21 
22  int ng = (solverChoice.terrain_type == TerrainType::EB) ? 3 : 1;
23 
24  const Real grav = solverChoice.gravity;
25 
26  Vector<MultiFab> gradp_temp; gradp_temp.resize(AMREX_SPACEDIM);
27  gradp_temp[0].define(vars_new[lev][Vars::xvel].boxArray(), lev_new[Vars::xvel].DistributionMap(), 1, 0);
28  gradp_temp[0].setVal(0.);
29  gradp_temp[1].define(vars_new[lev][Vars::yvel].boxArray(), lev_new[Vars::yvel].DistributionMap(), 1, 0);
30  gradp_temp[1].setVal(0.);
31  gradp_temp[2].define(vars_new[lev][Vars::zvel].boxArray(), lev_new[Vars::zvel].DistributionMap(), 1, 0);
32  gradp_temp[2].setVal(0.);
33 
34  int comp = 0;
35 
36  // The lateral boundary faces are included here. The base state in the ghost cells
37  // outside them is built at the height the mesh puts those cells at, rather than copied
38  // outward at constant index, so the gradient across a lateral domain face is a gradient
39  // of the reference atmosphere like any other and belongs in these extrema.
40  Box xface_domain = surroundingNodes(geom[lev].Domain(), 0);
41  Box yface_domain = surroundingNodes(geom[lev].Domain(), 1);
42 
43 
44  // Use this region to take max/min of gpz without including top and bottom faces
45  Box zface_domain = surroundingNodes(geom[lev].Domain(), 2);
46  int klo = zface_domain.smallEnd(2);
47  int khi = zface_domain.bigEnd(2);
48 
49  zface_domain.growLo(2,-1);
50  zface_domain.growHi(2,-1);
51 
52  // *******************************************************************************
53  // First check that base state satisfies EOS
54  // *******************************************************************************
55 
56  Print() << " " << std::endl;
57 
58  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp , 1);
59  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp , 1);
60  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp , 1);
61  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
62 
63  MultiFab dp(p_hse.boxArray(), p_hse.DistributionMap(), 1, 0);
64 
65  // Initialize to zero in case of EB covered cells
66  dp.setVal(0.);
67 
68  for (MFIter mfi(dp); mfi.isValid(); ++mfi) {
69  Box bx = mfi.validbox();
70  auto const rhse_arr = r_hse.const_array(mfi);
71  auto const phse_arr = p_hse.const_array(mfi);
72  auto const qvhse_arr = qv_hse.const_array(mfi);
73  auto const thhse_arr = th_hse.const_array(mfi);
74  auto dpeos_arr = dp.array(mfi);
75 
76  if (solverChoice.terrain_type != TerrainType::EB) {
77  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
78  Real rhotheta = rhse_arr(i,j,k) * thhse_arr(i,j,k);
79  dpeos_arr(i,j,k) = std::abs(getPgivenRTh(rhotheta, qvhse_arr(i,j,k)) - phse_arr(i,j,k));
80  });
81  } else {
82  Array4<const Real> volfrac = (get_eb(lev).get_const_factory())->getVolFrac().const_array(mfi);
83  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
84  if (volfrac(i,j,k) > zero) {
85  Real rhotheta = rhse_arr(i,j,k) * thhse_arr(i,j,k);
86  dpeos_arr(i,j,k) = std::abs(getPgivenRTh(rhotheta, qvhse_arr(i,j,k)) - phse_arr(i,j,k));
87  }
88  });
89  }
90  }
91  // This compares two pressures, so the tolerance has to be scaled to the
92  // pressure rather than fixed in Pa. The base state is O(1e5) Pa, where one
93  // single-precision ulp is already ~1e-2 Pa, so the absolute 1e-8 Pa bound
94  // used here previously could never be met in single precision even for a
95  // perfectly consistent base state. The double-precision coefficient below
96  // reproduces that 1e-8 bound at 1e5 Pa, so the check is unchanged there.
97 #ifdef AMREX_USE_FLOAT
98  const Real eos_rel_tol = Real(1.e-5);
99 #else
100  const Real eos_rel_tol = Real(1.e-13);
101 #endif
102  const Real p_hse_max = p_hse.max(0);
103  const Real eos_tol = eos_rel_tol * p_hse_max;
104 
105  Real max_diff = dp.max(0);
106  if (max_diff > eos_tol) {
107  IntVect max_loc = dp.maxIndex(0);
108  Print() << "Max value of |p_hse - p_eos| is " << max_diff << std::endl;
109  Print() << " with max in cell " << max_loc << std::endl;
110  Abort("Base state violates EOS ");
111  } else {
112  Print() << "Max value of |p_hse - p_eos| is " << max_diff
113  << ", less than the tolerance " << eos_tol << std::endl;
114  }
115 
116  // *******************************************************************************
117  // Now compute pressure gradients for base state pressure
118  // *******************************************************************************
119 
120  compute_gradp(p_hse, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
121  get_eb(lev), gradp_temp, solverChoice);
122 
123  Real min_gpx = gradp_temp[0].min(xface_domain,comp);
124  Real max_gpx = gradp_temp[0].max(xface_domain,comp);
125  if (max_gpx != zero || min_gpx != zero) {
126  Print() << "Min/max value of dp0/dx are " << min_gpx << " " << max_gpx << std::endl;
127  IntVect min_loc = gradp_temp[0].minIndex(comp);
128  IntVect max_loc = gradp_temp[0].maxIndex(comp);
129  amrex::Print() << " with min at face " << min_loc;
130  amrex::Print() << " with max at face " << max_loc;
131  Print() << std::endl;
132  } else {
133  Print() << "Min/max value of dp0/dx are zero " << std::endl;
134  }
135 
136  Real min_gpy = gradp_temp[1].min(yface_domain,comp);
137  Real max_gpy = gradp_temp[1].max(yface_domain,comp);
138  if (max_gpy != zero || min_gpy != zero) {
139  Print() << "Min/max value of dp0/dy are " << min_gpy << " " << max_gpy << std::endl;
140  IntVect min_loc = gradp_temp[1].minIndex(comp);
141  IntVect max_loc = gradp_temp[1].maxIndex(comp);
142  amrex::Print() << " with min at face " << min_loc;
143  amrex::Print() << " with max at face " << max_loc;
144  Print() << std::endl;
145  } else {
146  Print() << "Min/max value of dp0/dy are zero " << std::endl;
147  }
148 
149  if (solverChoice.terrain_type != TerrainType::EB) {
150  for (MFIter mfi(gradp_temp[2]); mfi.isValid(); ++mfi) {
151  Box bx = mfi.validbox();
152  if (bx.bigEnd(2) == khi) bx.growHi(2,-1);
153  if (bx.smallEnd(2) == 0 ) bx.growLo(2,-1);
154  auto gpz_arr = gradp_temp[2].array(mfi);
155  auto const rhse_arr = r_hse.const_array(mfi);
156  auto const qvhse_arr = qv_hse.const_array(mfi);
157  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
158  gpz_arr(i,j,k) += grav * myhalf * ( rhse_arr(i,j,k ) * (one + qvhse_arr(i,j,k ))
159  +rhse_arr(i,j,k-1) * (one + qvhse_arr(i,j,k-1)) );
160  });
161  }
162  // EB case: check HSE only for uncovered cells
163  } else {
164  for (MFIter mfi(gradp_temp[2]); mfi.isValid(); ++mfi) {
165  Box bx = mfi.validbox();
166  if (bx.bigEnd(2) == khi) bx.growHi(2,-1);
167  if (bx.smallEnd(2) == 0 ) bx.growLo(2,-1);
168  auto gpz_arr = gradp_temp[2].array(mfi);
169  auto const rhse_arr = r_hse.const_array(mfi);
170  auto const qvhse_arr = qv_hse.const_array(mfi);
171  Array4<const Real> w_volfrac = (get_eb(lev).get_w_const_factory())->getVolFrac().const_array(mfi);
172 
173  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
174  if (w_volfrac(i,j,k) > zero) {
175  gpz_arr(i,j,k) += grav * myhalf * ( rhse_arr(i,j,k ) * (one + qvhse_arr(i,j,k ))
176  +rhse_arr(i,j,k-1) * (one + qvhse_arr(i,j,k-1)) );
177  }
178  });
179  }
180  }
181 
182  // The residual tested below, dp0/dz + rho0*|g|, is a cancellation between two
183  // terms of size rho0*|g|. The smallest value it can take is therefore set by
184  // the roundoff in p0 spread over one vertical cell, eps*|p0|/dz. That floor
185  // is grid dependent -- in single precision it is ~1e-4 Pa/m at dz = 100 m but
186  // ~3e-4 Pa/m at dz = 40 m -- so no fixed tolerance can work across the range
187  // of meshes the tests cover. Use the larger of the historical fixed value and
188  // that floor. In double the floor is O(1e-12), so the fixed value always wins
189  // and this check keeps exactly its previous behavior.
190  const Real tol_fixed = (solverChoice.terrain_type == TerrainType::EB) ? Real(1.e-4)
191  : Real(1.e-8);
192 
193  const Real dz_for_tol = (lev < static_cast<int>(dz_min.size()) && dz_min[lev] > Real(0))
194  ? dz_min[lev] : geom[lev].CellSize(2);
195 
196  const Real tol_roundoff = Real(16) * std::numeric_limits<Real>::epsilon()
197  * p_hse_max / dz_for_tol;
198 
199  const Real tol = std::max(tol_fixed, tol_roundoff);
200 
201  Real min_gpz = gradp_temp[2].min(zface_domain,comp);
202  Real max_gpz = gradp_temp[2].max(zface_domain,comp);
203 
204  if (std::abs(max_gpz) > tol || std::abs(min_gpz) > tol) {
205  IntVect min_loc = gradp_temp[2].minIndex(comp);
206  IntVect max_loc = gradp_temp[2].maxIndex(comp);
207  Print() << "Min/max value of dp0/dz + rho0*|g| are " << min_gpz << " " << max_gpz;
208  if (min_loc[2] != klo && min_loc[2] != khi) amrex::Print() << " with min at face " << min_loc;
209  if (max_loc[2] != klo && max_loc[2] != khi) amrex::Print() << " with max at face " << max_loc;
210  amrex::Abort("Base state is too far out of HSE");
211  } else {
212  Print() << "Min/max value of dp0/dz + rho0*|g| are less than " << tol << std::endl;
213  }
214  Print() << " " << std::endl;
215 
216  if (!solverChoice.anelastic[lev]) {
217 
218  // *******************************************************************************
219  // Now compute for full (moist) pressure
220  // *******************************************************************************
221 
222  MultiFab p(p_hse.boxArray(), p_hse.DistributionMap(), 1, ng);
223  MultiFab rho(lev_new[Vars::cons], make_alias, Rho_comp , 1);
224 
225  if (solverChoice.moisture_type != MoistureType::None) {
226 
227  for (MFIter mfi(rho); mfi.isValid(); ++mfi)
228  {
229  Box gbx = mfi.tilebox();
230  gbx.grow(IntVect(ng,ng,ng));
231  if (gbx.smallEnd(2) < 0) gbx.setSmall(2,0);
232 
233  const Array4<const Real>& cell_data = lev_new[Vars::cons].array(mfi);
234  const Array4<const Real>& r_arr = rho.array(mfi);
235  const Array4< Real>& pp_arr = p.array(mfi);
236  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
237  {
238  Real qv_for_p = cell_data(i,j,k,RhoQ1_comp)/r_arr(i,j,k);
239  pp_arr(i,j,k) = getPgivenRTh(cell_data(i,j,k,RhoTheta_comp),qv_for_p);
240  });
241  }
242  compute_gradp(p, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
243  get_eb(lev), gradp_temp, solverChoice);
244 
245  min_gpx = gradp_temp[0].min(xface_domain,comp);
246  max_gpx = gradp_temp[0].max(xface_domain,comp);
247  if (max_gpx != zero || min_gpx != zero) {
248  Print() << "Min/Max value of x-gradient of full (moist) pressure are " << min_gpx << " " << max_gpx;
249  IntVect min_loc = gradp_temp[0].minIndex(comp);
250  IntVect max_loc = gradp_temp[0].maxIndex(comp);
251  amrex::Print() << " with min at face " << min_loc;
252  amrex::Print() << " with max at face " << max_loc;
253  Print() << std::endl;
254  } else {
255  Print() << "Min/max value of x-gradient of full (moist) pressure are zero " << std::endl;
256  }
257 
258  min_gpy = gradp_temp[1].min(yface_domain,comp);
259  max_gpy = gradp_temp[1].max(yface_domain,comp);
260  if (max_gpy != zero || min_gpy != zero) {
261  Print() << "Min/Max value of y-gradient of full (moist) pressure are " << min_gpy << " " << max_gpy;
262  IntVect min_loc = gradp_temp[1].minIndex(comp);
263  IntVect max_loc = gradp_temp[1].maxIndex(comp);
264  amrex::Print() << " with min at face " << min_loc;
265  amrex::Print() << " with max at face " << max_loc;
266  Print() << std::endl;
267  } else {
268  Print() << "Min/max value of y-gradient of full (moist) pressure are zero " << std::endl;
269  }
270 
271  MultiFab qt(rho.boxArray(), rho.DistributionMap(), 1, 1);
272  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
273  make_qt(lev_new[Vars::cons], qt, n_qstate_into_total);
274 
275  for (MFIter mfi(gradp_temp[2]); mfi.isValid(); ++mfi)
276  {
277  Box bx = mfi.validbox();
278  if (bx.bigEnd(2) == khi) bx.growHi(2,-1);
279  if (bx.smallEnd(2) == 0 ) bx.growLo(2,-1);
280  auto gpz_arr = gradp_temp[2].array(mfi);
281  auto const r_arr = rho.const_array(mfi);
282  auto const qt_arr = qt.const_array(mfi);
283 
284  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
285  gpz_arr(i,j,k) += grav * myhalf * (r_arr(i,j,k )*(one+qt_arr(i,j,k )) +
286  r_arr(i,j,k-1)*(one+qt_arr(i,j,k-1)) );
287  });
288  }
289 
290  min_gpz = gradp_temp[2].min(zface_domain,comp);
291  max_gpz = gradp_temp[2].max(zface_domain,comp);
292  if (max_gpz != zero || min_gpz != zero) {
293  IntVect min_loc = gradp_temp[2].minIndex(comp);
294  IntVect max_loc = gradp_temp[2].maxIndex(comp);
295  Print() << "Min/max value of moist dp/dz + rho_m*|g| are " << min_gpz << " " << max_gpz;
296  if (min_loc[2] != klo && min_loc[2] != khi) amrex::Print() << " with min at face " << min_loc;
297  if (max_loc[2] != klo && max_loc[2] != khi) amrex::Print() << " with max at face " << max_loc;
298  } else {
299  Print() << "Min/max value of moist dp/dz + rho_m*|g| are zero " << std::endl;
300  }
301  Print() << " " << std::endl;
302  } // if moist
303  } // if !anelastic
304 }
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
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)
Compute the full pressure gradient.
Definition: ERF_MakeGradP.cpp:124
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
amrex::Vector< amrex::Real > dz_min
Definition: ERF.H:1864
eb_aux_ const * get_w_const_factory() const noexcept
Return the ERF auxiliary z-face EB factory.
Definition: ERF_EB.H:133
@ qv0_comp
Definition: ERF_IndexDefines.H:80
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
amrex::Real gravity
Effective gravitational acceleration.
Definition: ERF_DataStruct.H:2060
Here is the call graph for this function:

◆ compute_native_shoc_tendencies()

void ERF::compute_native_shoc_tendencies ( int  lev,
amrex::MultiFab *  cons,
amrex::MultiFab *  xvel,
amrex::MultiFab *  yvel,
amrex::MultiFab *  zvel,
amrex::Real w_subsid,
amrex::MultiFab *  tau13,
amrex::MultiFab *  tau23,
amrex::MultiFab *  hfx3,
amrex::MultiFab *  qfx3,
amrex::MultiFab *  eddyDiffs,
amrex::MultiFab *  z_phys_nd,
const double &  dt_advance 
)
19 {
21  native_shoc_driver[lev]->advance(*cons, *xvel, *yvel, *zvel,
22  tau13, tau23, hfx3, qfx3, eddyDiffs,
23  *z_phys_nd_in, Geom(lev), dt_advance);
24 }
Here is the call graph for this function:

◆ ComputeAndWriteEnsemblePerturbations()

void ERF::ComputeAndWriteEnsemblePerturbations ( const int  da_iter)

◆ ComputeDt()

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

Function that calls estTimeStep for each level

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

Referenced by EvolveOneStep().

Here is the caller graph for this function:

◆ ComputeGhostCells()

static AMREX_FORCE_INLINE int ERF::ComputeGhostCells ( const SolverChoice sc)
inlinestaticprivate
1869  {
1870  // We never have fewer than 2 ghost cells
1871  int ngrow = 2;
1872 
1873  // This sets the minimum to 3 if using num_diff but we might still need more
1874  if (sc.use_num_diff) {
1875  ngrow = 3;
1876  }
1877 
1878  // With EB we always have 4 so no need to do further tests
1879  if (sc.terrain_type == TerrainType::EB) {
1880  ngrow = 4;
1881  return ngrow;
1882  }
1883 
1884  if (
1891  { ngrow = 3; }
1892  else if (
1899  { ngrow = 3; }
1900  else if (
1907  { ngrow = 3; }
1908  else if (
1915  { ngrow = 3; }
1916  else if (
1923  { ngrow = 3; }
1924  else if (
1931  { ngrow = 4; }
1932 
1933  return ngrow;
1934  }
@ Centered_6th
AdvType dycore_vert_adv_type
Vertical advection scheme for dynamical-core variables.
Definition: ERF_AdvStruct.H:442
AdvType moistscal_vert_adv_type
Vertical advection scheme for moist scalar variables.
Definition: ERF_AdvStruct.H:446
AdvType dryscal_horiz_adv_type
Horizontal advection scheme for dry scalar variables.
Definition: ERF_AdvStruct.H:443
AdvType dycore_horiz_adv_type
Horizontal advection scheme for dynamical-core variables.
Definition: ERF_AdvStruct.H:441
AdvType moistscal_horiz_adv_type
Horizontal advection scheme for moist scalar variables.
Definition: ERF_AdvStruct.H:445
AdvType dryscal_vert_adv_type
Vertical advection scheme for dry scalar variables.
Definition: ERF_AdvStruct.H:444
AdvChoice advChoice
Advection-related options.
Definition: ERF_DataStruct.H:1970

◆ ComputeGlobalMinLocation()

void ERF::ComputeGlobalMinLocation ( const SolverChoice sc,
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 
)

Compute the global minimum and its location across all ranks.

Parameters
[in]scSolver choices
[in]lev_geomGeometry of the current level
[in]S_dataConservative state data
[in]d_val_min_ptrDevice pointer to local minimum value
[in]d_i_min_ptrDevice pointer to local minimum i-index
[in]d_j_min_ptrDevice pointer to local minimum j-index
[out]global_val_minGlobal minimum value
[out]global_i_minGlobal minimum i-index
[out]global_j_minGlobal minimum j-index
86 {
87  Real h_val_min;
88  int h_i_min, h_j_min;
89 
90  Gpu::copy(Gpu::deviceToHost, d_val_min_ptr, d_val_min_ptr + 1, &h_val_min);
91  Gpu::copy(Gpu::deviceToHost, d_i_min_ptr, d_i_min_ptr + 1, &h_i_min);
92  Gpu::copy(Gpu::deviceToHost, d_j_min_ptr, d_j_min_ptr + 1, &h_j_min);
93  Gpu::synchronize();
94 
95  Real local_val_min = h_val_min;
96  int local_i_min = h_i_min;
97  int local_j_min = h_j_min;
98 
99  int rank = ParallelDescriptor::MyProc();
100 
101  // NOTE: reduce through the amrex wrappers rather than a hard-coded
102  // MPI_DOUBLE_INT MINLOC. The latter is a type mismatch whenever
103  // amrex::Real is float (ERF_PRECISION=SINGLE), in which case MPI
104  // reads and writes 8 bytes of a 4-byte member. Reducing the value
105  // and then taking the smallest rank that attains it reproduces
106  // MPI_MINLOC's lowest-rank tie-break without naming an MPI type.
107  global_val_min = local_val_min;
108  ParallelDescriptor::ReduceRealMin(global_val_min);
109 
110  int owner_rank = (local_val_min == global_val_min) ? rank : ParallelDescriptor::NProcs();
111  ParallelDescriptor::ReduceIntMin(owner_rank);
112  AMREX_ALWAYS_ASSERT(owner_rank < ParallelDescriptor::NProcs());
113 
114  // Broadcast the indices from the rank that owns the minimum
115  global_i_min = local_i_min;
116  global_j_min = local_j_min;
117 
118  ParallelDescriptor::Bcast(&global_i_min, 1, owner_rank);
119  ParallelDescriptor::Bcast(&global_j_min, 1, owner_rank);
120 
121  if (rank == 0) {
122  Print() << "Global minimum distance to hurricane eye (k=0): "
123  << global_val_min << " at (i,j) = ("
124  << global_i_min << ", " << global_j_min << ")\n";
125  }
126 
127  Gpu::DeviceScalar<Real> d_eye_lat(zero), d_eye_lon(zero);
128 
129  Real* d_eye_lat_ptr = d_eye_lat.dataPtr();
130  Real* d_eye_lon_ptr = d_eye_lon.dataPtr();
131 
132  int levc = finest_level;
133  // On owner_rank, compute eye_lat and eye_lon
134  if (sc.init_type == InitType::WRFInput and rank == owner_rank) {
135  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
136  const Box& box = mfi.validbox();
137  FArrayBox& fab_lat = (*(lat_m[levc]))[mfi];
138  FArrayBox& fab_lon = (*(lon_m[levc]))[mfi];
139  const Array4<Real>& lat_arr = fab_lat.array();
140  const Array4<Real>& lon_arr = fab_lon.array();
141 
142  if (box.smallEnd()[2] == 0) {
143  Box bx2d = makeSlab(box,2,0);
144  ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int ) {
145  if (i == global_i_min && j == global_j_min) {
146  *d_eye_lat_ptr = lat_arr(i,j,0);
147  *d_eye_lon_ptr = lon_arr(i,j,0);
148  }
149  });
150  }
151  }
152  }
153 
154  if (sc.init_type == InitType::HindCast and rank == owner_rank) {
155  // On owner_rank, compute eye_lat and eye_lon
156  if (rank == owner_rank) {
157  for (amrex::MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
158  const amrex::Box& box = mfi.validbox();
159  const auto& mf_latlon = forecast_state_interp[levc][4];
160  const auto latlon_arr = mf_latlon.array(mfi);
161  amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
162  if (i == global_i_min && j == global_j_min && k == 0) {
163  *d_eye_lat_ptr = latlon_arr(i,j,k,0);
164  *d_eye_lon_ptr = latlon_arr(i,j,k,1);
165  }
166  });
167  }
168  }
169  }
170 
171  Real eye_lat = d_eye_lat.dataValue();
172  Real eye_lon = d_eye_lon.dataValue();
173 
174  // Synchronize to ensure the owner has computed values
175  Gpu::synchronize();
176 
177  ParallelDescriptor::Bcast(&eye_lat, 1, owner_rank);
178  ParallelDescriptor::Bcast(&eye_lon, 1, owner_rank);
179 
180  const auto dx = lev_geom.CellSizeArray();
181  const auto prob_lo = lev_geom.ProbLoArray();
182 
183  Real eye_x = prob_lo[0] + (global_i_min+myhalf)*dx[0];
184  Real eye_y = prob_lo[1] + (global_j_min+myhalf)*dx[1];
185 
186  hurricane_eye_track_xy.push_back({eye_x, eye_y});
187  hurricane_eye_track_latlon.push_back({eye_lon, eye_lat});
188 }
const GpuArray< Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_InitCustomPert_CloudChamber.H:33
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_eye_track_xy
Definition: ERF.H:288
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_interp
Definition: ERF.H:296
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_eye_track_latlon
Definition: ERF.H:289
static InitType init_type
Initial-condition source selected for the run.
Definition: ERF_DataStruct.H:1943
Here is the call graph for this function:

◆ ConfigureDriverAtmosToOceanCoupling()

void ERF::ConfigureDriverAtmosToOceanCoupling ( bool  use_coupling_driver,
bool  use_two_way_coupling,
bool  use_state_contract 
)
250 {
251  m_driver_has_atm2ocn_coupling = use_coupling_driver;
252  m_driver_uses_two_way_coupling = use_two_way_coupling;
253  m_driver_atm2ocn_uses_state_contract = use_state_contract;
254 }
bool m_driver_has_atm2ocn_coupling
Definition: ERF.H:950
bool m_driver_uses_two_way_coupling
Definition: ERF.H:951
bool m_driver_atm2ocn_uses_state_contract
Definition: ERF.H:952

◆ Construct_ERFFillPatchers()

void ERF::Construct_ERFFillPatchers ( int  lev)
private
3548 {
3549  auto& fine_new = vars_new[lev];
3550  auto& crse_new = vars_new[lev-1];
3551  auto& ba_fine = fine_new[Vars::cons].boxArray();
3552  auto& ba_crse = crse_new[Vars::cons].boxArray();
3553  auto& dm_fine = fine_new[Vars::cons].DistributionMap();
3554  auto& dm_crse = crse_new[Vars::cons].DistributionMap();
3555 
3556  int ncomp = vars_new[lev][Vars::cons].nComp();
3557 
3558  FPr_c.emplace_back(ba_fine, dm_fine, geom[lev] ,
3559  ba_crse, dm_crse, geom[lev-1],
3560  -cf_width, -cf_set_width, ncomp, &cell_cons_interp);
3561  FPr_u.emplace_back(convert(ba_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
3562  convert(ba_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
3563  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
3564  FPr_v.emplace_back(convert(ba_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
3565  convert(ba_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
3566  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
3567  FPr_w.emplace_back(convert(ba_fine, IntVect(0,0,1)), dm_fine, geom[lev] ,
3568  convert(ba_crse, IntVect(0,0,1)), dm_crse, geom[lev-1],
3569  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
3570 }
int cf_set_width
Definition: ERF.H:1333

◆ 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 
)

Build ensemble background perturbation MultiFabs from custom coarse data.

Parameters
levInteger specifying the current level
mf_cc_pertCell-centered perturbations to apply to the background
cons_pertConserved-state perturbation MultiFab to fill
xvel_pertx-face velocity perturbation MultiFab to fill
yvel_perty-face velocity perturbation MultiFab to fill
zvel_pertz-face velocity perturbation MultiFab to fill
738 {
739 
740  ignore_unused(lev);
741  int nx_crse, ny_crse, nz_crse, ng_crse, ncomp_crse;
742  Vector<Vector<Real>> data_crse;
743  std::array<Real,3> problo_ext, probhi_ext;
744 
745  Vector<Real> data_rho, data_theta, data_xvel, data_yvel, data_zvel;
746  Vector<Real> data_qv, data_qc, data_qrain;
747 
749  nx_crse, ny_crse, nz_crse, ng_crse, ncomp_crse,
750  problo_ext, probhi_ext,
751  data_rho, data_theta,
752  data_xvel, data_yvel, data_zvel,
753  data_qv, data_qc, data_qrain);
754 
755  Geometry& geom_fine = geom[0];
756  // Create a cell-centered multifab on the fine mesh - ie. something with the same boxarray,
757  // distributed mapping, nGrow, but with 5 components
758  MultiFab mf_cc_fine;
759  const MultiFab& src = vars_new[0][0];
760  int ncomp = 8;
761  mf_cc_fine.define(src.boxArray(), src.DistributionMap(),
762  ncomp, src.nGrow());
763 
764  InterpolateToFineMF(data_rho, data_theta,
765  data_xvel, data_yvel, data_zvel,
766  data_qv, data_qc, data_qrain,
767  nx_crse, ny_crse, nz_crse,
768  problo_ext, probhi_ext,
769  mf_cc_fine,
770  geom_fine);
771 
772 
773  Vector<std::string> varnames = {"density","theta", "x_velocity","y_velocity","z_velocity", "qv", "qc", "qrain"};
774  WriteSingleLevelPlotfile("1_plt_final_interp", mf_cc_fine, varnames, geom_fine, zero, 0);
775 
776  ApplyNeumannBCs(geom_fine, mf_cc_fine);
777  // Add pertubrations stored in the "pert" variables in the function arguments
778  // (multiplied by the corresponding amplitude)
779  AddPertToBckgnd(mf_cc_fine, mf_cc_pert, solverChoice.ens_pert_amplitude);
780  ApplyNeumannBCs(geom_fine, mf_cc_fine);
781 
782  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
783  int n_qstate_moist = 0;
784  if (use_moisture) {
785  n_qstate_moist = micro->Get_Qstate_Moist_Size();
786  }
787 
788  MakeFinalMultiFabs(mf_cc_fine, cons_pert, xvel_pert, yvel_pert, zvel_pert, n_qstate_moist);
789 }
const bool use_moisture
Definition: ERF_InitCustomPert_ABL.H:71
void MakeFinalMultiFabs(const MultiFab &mf_cc_fine, MultiFab &cons_pert, MultiFab &xvel_pert, MultiFab &yvel_pert, MultiFab &zvel_pert, const int n_qstate_moist)
Definition: ERF_InitForEnsemble.cpp:612
void AddPertToBckgnd(MultiFab &mf_cc_fine, const MultiFab &mf_cc_pert, const Real &ens_pert_amplitude)
Definition: ERF_InitForEnsemble.cpp:690
void ApplyNeumannBCs(const Geometry &geom, MultiFab &mf_cc)
Definition: ERF_InitForEnsemble.cpp:216
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, const Vector< Real > &data_qv, const Vector< Real > &data_qc, const Vector< Real > &data_qrain, 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:468
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, Vector< Real > &data_qv, Vector< Real > &data_qc, Vector< Real > &data_qrain)
Definition: ERF_InitForEnsemble.cpp:292
std::string coarse_bckgnd_data_file
Coarse background data file used for ensemble initialization.
Definition: ERF_DataStruct.H:2327
amrex::Real ens_pert_amplitude
Amplitude of ensemble initialization perturbations.
Definition: ERF_DataStruct.H:2326
Here is the call graph for this function:

◆ create_random_perturbations()

void ERF::create_random_perturbations ( const int  lev,
amrex::MultiFab &  mf_cc_pert 
)

Create a cell-centered MultiFab of random perturbations for one AMR level.

Parameters
levInteger specifying the current level
mf_cc_pertMultiFab filled with random perturbation components
35 {
36  const MultiFab& src = vars_new[lev][Vars::cons];
37 
38  int ncomp = 8;
39  mf_cc_pert.define(src.boxArray(), src.DistributionMap(),
40  ncomp, src.nGrow());
41 
42  // Loop over cell-centered boxes
43  for (MFIter mfi(mf_cc_pert, TilingIfNotGPU()); mfi.isValid(); ++mfi)
44  {
45  const Box& bx = mfi.tilebox();
46 
47  auto const& pert_arr = mf_cc_pert.array(mfi);
48 
49  // Loop over all 5 components
50  amrex::ParallelForRNG(bx, ncomp,
51  [=] AMREX_GPU_DEVICE (int i, int j, int k, int n,
52  const amrex::RandomEngine& engine) noexcept
53  {
54  pert_arr(i,j,k,n) = amrex::Real(2.0) * amrex::Random(engine) - amrex::Real(1.0);
55  });
56  }
57 }
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;if(deterministic_ic_pert) { rand_double=erf_hash_rng::hash_uniform(i, j, k, temperature_hash_comp, lev, ic_pert_seed);} else { 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);} } if(add_sinusoidal_T) { const Real zl=z/pert_ref_height;const Real damp=std::exp(-myhalf *zl *zl);const Real rho=state(i, j, k, Rho_comp);const Real rhotheta=state(i, j, k, RhoTheta_comp);const Real qv=(use_moisture) ? state(i, j, k, RhoQ1_comp)/rho :amrex::Real(0);const Real Told=getTgivenRandRTh(rho, rhotheta, qv);const Real P=getPgivenRTh(rhotheta, qv);const Real Tnew=Told+tfac *damp *z *std::cos(cval *(x - xc));const Real theta_new=getThgivenTandP(Tnew, P, rdOcp);const Real rho_new=getRhogivenThetaPress(theta_new, P, rdOcp, qv);state_pert(i, j, k, Rho_comp)+=rho_new - rho;if(use_moisture) { state_pert(i, j, k, RhoQ1_comp)+=(rho_new - rho) *qv;} } 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) { const Real z_sfc=(use_eb) ? zero :fourth *(z_nd(i, j, klo)+z_nd(i+1, j, klo)+z_nd(i, j+1, klo)+z_nd(i+1, j+1, klo));const Real z_agl=z_cc(i, j, k) - z_sfc;state_pert(i, j, k, RhoKE_comp) *=amrex::max(std::pow(1 - amrex::min(z_agl/KE_decay_height, amrex::Real(1)), KE_decay_order), amrex::Real(1e-12));} } })
Here is the call graph for this function:

◆ DataLog()

AMREX_FORCE_INLINE std::ostream& ERF::DataLog ( int  i)
inlineprivate
1945  {
1946  return *datalog[i];
1947  }
amrex::Vector< std::unique_ptr< std::fstream > > datalog
Definition: ERF.H:2133

◆ DataLogName()

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

The filename of the ith datalog file.

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

◆ define_column_kextent()

void ERF::define_column_kextent ( int  lev,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
private

Build the map of the vertical extent of the grid column over each (i,j).

The implicit vertical diffusion solves invert one tridiagonal system per column, so they are only well posed if each column lives in a single box. We enforce that here, then record each column's [klo,khi] on a z-slab with a one-cell halo in x and y; a box can then read the vertical extent of the column on the other side of any of its faces, which is what the staggered (u,v) solves need to agree with their neighbors.

Parameters
[in]levlevel of refinement
[in]baBoxArray at this level
[in]dmDistributionMapping at this level
638 {
639  if (grids_are_split_in_z(ba))
640  {
641  bool implicit_var = (solverChoice.implicit_thermal_diffusion ||
643  bool implicit_stage = false;
644  if (lev < solverChoice.vert_implicit_fac.size()) {
645  for (int nrk(0); nrk < solverChoice.vert_implicit_fac[lev].size(); ++nrk) {
646  if (solverChoice.vert_implicit_fac[lev][nrk] > zero) { implicit_stage = true; }
647  }
648  }
649  if (implicit_var && implicit_stage) {
650  Abort("The grids at level " + std::to_string(lev) + " are decomposed in the vertical, "
651  "which cannot be combined with implicit vertical diffusion: the solve inverts one "
652  "tridiagonal system per column, and a column split across boxes would instead be "
653  "solved piecewise with spurious internal boundaries, giving an answer that depends "
654  "on the grid decomposition. Either set erf.vert_implicit_fac = 0 0 0 (or turn off "
655  "erf.implicit_thermal_diffusion and erf.implicit_momentum_diffusion), or choose "
656  "grids that are not split in z.");
657  }
658  }
659 
660  column_kextent[lev] = std::make_unique<iMultiFab>(ba2d[lev], dm, 2, IntVect(1,1,0));
661 
662  // Columns that no box covers -- outside the level, or outside the domain -- keep these
663  // sentinels, which drop out of the max/min that defines the solve range below.
664  column_kextent[lev]->setVal(column_kextent_lo_sentinel, 0, 1, IntVect(1,1,0));
665  column_kextent[lev]->setVal(column_kextent_hi_sentinel, 1, 1, IntVect(1,1,0));
666 
667  // NOTE: no ParallelFor here. This is a private member function, and nvcc does not allow
668  // an extended (__device__) lambda inside a member function with private or protected
669  // access. Each box contributes a single (klo,khi) pair over its whole footprint,
670  // so BaseFab::setVal fills it on the device without needing a lambda at all.
671  for (MFIter mfi(*column_kextent[lev]); mfi.isValid(); ++mfi)
672  {
673  const Box& vbx = mfi.validbox();
674  const Box& bx3d = ba[mfi.index()];
675  IArrayBox& kext_fab = (*column_kextent[lev])[mfi];
676  kext_fab.setVal<RunOn::Device>(bx3d.smallEnd(2), vbx, 0, 1);
677  kext_fab.setVal<RunOn::Device>(bx3d.bigEnd(2) , vbx, 1, 1);
678  }
679 
680  column_kextent[lev]->FillBoundary(geom[lev].periodicity());
681 }
static constexpr int column_kextent_hi_sentinel
Definition: ERF.H:1750
static constexpr int column_kextent_lo_sentinel
Definition: ERF.H:1749
static bool grids_are_split_in_z(const amrex::BoxArray &ba)
Definition: ERF_MakeNewArrays.cpp:613
bool implicit_thermal_diffusion
Whether implicit vertical thermal diffusion is included.
Definition: ERF_DataStruct.H:2002
amrex::Vector< amrex::Vector< amrex::Real > > vert_implicit_fac
Per-level, per-stage implicit vertical diffusion factors.
Definition: ERF_DataStruct.H:1999
bool implicit_momentum_diffusion
Whether implicit vertical momentum diffusion is included.
Definition: ERF_DataStruct.H:2005

◆ Define_ERFFillPatchers()

void ERF::Define_ERFFillPatchers ( int  lev)
private
3574 {
3575  auto& fine_new = vars_new[lev];
3576  auto& crse_new = vars_new[lev-1];
3577  auto& ba_fine = fine_new[Vars::cons].boxArray();
3578  auto& ba_crse = crse_new[Vars::cons].boxArray();
3579  auto& dm_fine = fine_new[Vars::cons].DistributionMap();
3580  auto& dm_crse = crse_new[Vars::cons].DistributionMap();
3581 
3582  int ncomp = fine_new[Vars::cons].nComp();
3583 
3584  FPr_c[lev-1].Define(ba_fine, dm_fine, geom[lev] ,
3585  ba_crse, dm_crse, geom[lev-1],
3586  -cf_width, -cf_set_width, ncomp, &cell_cons_interp);
3587  FPr_u[lev-1].Define(convert(ba_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
3588  convert(ba_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
3589  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
3590  FPr_v[lev-1].Define(convert(ba_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
3591  convert(ba_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
3592  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
3593  FPr_w[lev-1].Define(convert(ba_fine, IntVect(0,0,1)), dm_fine, geom[lev] ,
3594  convert(ba_crse, IntVect(0,0,1)), dm_crse, geom[lev-1],
3595  -cf_width, -cf_set_width, 1, &face_cons_linear_interp);
3596 }

◆ DerDataLog()

AMREX_FORCE_INLINE std::ostream& ERF::DerDataLog ( int  i)
inlineprivate
1952  {
1953  return *der_datalog[i];
1954  }
amrex::Vector< std::unique_ptr< std::fstream > > der_datalog
Definition: ERF.H:2134

◆ DerDataLogName()

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

◆ 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
timeCurrent simulation time
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_KmvProfile for vertical turbulent viscosity on Host
h_avg_KhvProfile for vertical scalar diffusivity on Host
h_avg_qvProfile for water vapor on Host
h_avg_qcProfile for cloud water on Host
h_avg_qrProfile for rain water on Host
h_avg_wqvProfile for vertical velocity * water vapor on Host
h_avg_wqcProfile for vertical velocity * cloud water on Host
h_avg_wqrProfile for vertical velocity * rain water on Host
h_avg_qiProfile for cloud ice on Host
h_avg_qsProfile for snow on Host
h_avg_qgProfile for graupel 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_vthProfile for y-velocity * potential temperature on Host
h_avg_wthProfile for z-velocity * potential temperature on Host
h_avg_ththProfile for potential temperature squared on Host The definition also carries u_i*u_i velocity triple-product accumulators.
h_avg_kuProfile for resolved x-momentum kinetic-energy flux on Host
h_avg_kvProfile for resolved y-momentum kinetic-energy flux on Host
h_avg_kwProfile for resolved z-momentum kinetic-energy flux 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
h_avg_wthvProfile for vertical velocity * virtual potential temperature on Host
256 {
257  // We assume that this is always called at level 0
258  int lev = 0;
259 
260  bool l_use_kturb = solverChoice.turbChoice[lev].use_kturb;
261  bool l_use_KE = solverChoice.turbChoice[lev].use_tke;
262  // This will hold rho, theta, ksgs, Kmh, Kmv, uu, uv, uw, vv, vw, ww, uth, vth, wth,
263  // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
264  // thth, uiuiu, uiuiv, uiuiw, p, pu, pv, pw, qv, qc, qr, wqv, wqc, wqr,
265  // 14 15 16 17 18 19 20 21 22 23 24 25 26 27
266  // qi, qs, qg, wthv
267  // 28 29 30 31
268  MultiFab mf_out(grids[lev], dmap[lev], 32, 0);
269 
270  MultiFab mf_vels(grids[lev], dmap[lev], AMREX_SPACEDIM, 0);
271 
272  MultiFab u_cc(mf_vels, make_alias, 0, 1); // u at cell centers
273  MultiFab v_cc(mf_vels, make_alias, 1, 1); // v at cell centers
274  MultiFab w_cc(mf_vels, make_alias, 2, 1); // w at cell centers
275 
276  average_face_to_cellcenter(mf_vels,0,
277  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],&vars_new[lev][Vars::yvel],&vars_new[lev][Vars::zvel]});
278 
279  int zdir = 2;
280  auto domain = geom[0].Domain();
281 
282  // Sum in the horizontal plane
283  h_avg_u = sumToLine(mf_vels ,0,1,domain,zdir);
284  h_avg_v = sumToLine(mf_vels ,1,1,domain,zdir);
285  h_avg_w = sumToLine(mf_vels ,2,1,domain,zdir);
286 
287  int hu_size = h_avg_u.size();
288 
289  // Divide by the total number of cells we are averaging over
290  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
291  for (int k = 0; k < hu_size; ++k) {
292  h_avg_u[k] /= area_z; h_avg_v[k] /= area_z; h_avg_w[k] /= area_z;
293  }
294 
295  Gpu::DeviceVector<Real> d_avg_u(hu_size, zero);
296  Gpu::DeviceVector<Real> d_avg_v(hu_size, zero);
297  Gpu::DeviceVector<Real> d_avg_w(hu_size, zero);
298 
299 #if 0
300  auto* avg_u_ptr = d_avg_u.data();
301  auto* avg_v_ptr = d_avg_v.data();
302  auto* avg_w_ptr = d_avg_w.data();
303 #endif
304 
305  Gpu::copy(Gpu::hostToDevice, h_avg_u.begin(), h_avg_u.end(), d_avg_u.begin());
306  Gpu::copy(Gpu::hostToDevice, h_avg_v.begin(), h_avg_v.end(), d_avg_v.begin());
307  Gpu::copy(Gpu::hostToDevice, h_avg_w.begin(), h_avg_w.end(), d_avg_w.begin());
308 
309  int nvars = vars_new[lev][Vars::cons].nComp();
310  MultiFab mf_cons(vars_new[lev][Vars::cons], make_alias, 0, nvars);
311 
312  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
313 
314  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
315  const MultiFab* eta_src = nullptr;
316  const bool have_native_shoc_diagnostics =
317  solverChoice.turbChoice[lev].uses_native_shoc() &&
318  native_shoc_driver[lev] &&
319  native_shoc_driver[lev]->has_native_diagnostics();
320  if (l_use_kturb) {
321  if (have_native_shoc_diagnostics) {
322  eta_src = &native_shoc_driver[lev]->native_diagnostics();
323  } else
324  {
325  eta_src = eddyDiffs_lev[lev].get();
326  }
327  }
328 
329  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
330  {
331  const Box& bx = mfi.tilebox();
332  const Array4<Real>& fab_arr = mf_out.array(mfi);
333  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
334  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
335  const Array4<Real>& w_cc_arr = w_cc.array(mfi);
336  const Array4<Real>& cons_arr = mf_cons.array(mfi);
337  const Array4<Real>& p0_arr = p_hse.array(mfi);
338  const Array4<const Real>& eta_arr = (eta_src) ? eta_src->const_array(mfi) :
339  Array4<const Real>{};
340 
341  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
342  {
343  Real theta = cons_arr(i,j,k,RhoTheta_comp) / cons_arr(i,j,k,Rho_comp);
344  fab_arr(i, j, k, 0) = cons_arr(i,j,k,Rho_comp);
345  fab_arr(i, j, k, 1) = theta;
346  Real ksgs = zero;
347  if (l_use_KE) {
348  ksgs = cons_arr(i,j,k,RhoKE_comp) / cons_arr(i,j,k,Rho_comp);
349  }
350  fab_arr(i, j, k, 2) = ksgs;
351 #if 1
352  if (l_use_kturb) {
353  fab_arr(i, j, k, 3) = eta_arr(i,j,k,EddyDiff::Mom_v); // Kmv
354  fab_arr(i, j, k, 4) = eta_arr(i,j,k,EddyDiff::Theta_v); // Khv
355  } else {
356  fab_arr(i, j, k, 3) = zero;
357  fab_arr(i, j, k, 4) = zero;
358  }
359 #else
360  // Here we hijack the "Kturb" variable name to print out the resolved kinetic energy
361  Real upert = u_cc_arr(i,j,k) - avg_u_ptr[k];
362  Real vpert = v_cc_arr(i,j,k) - avg_v_ptr[k];
363  Real wpert = w_cc_arr(i,j,k) - avg_w_ptr[k];
364  fab_arr(i, j, k, 3) = myhalf * (upert*upert + vpert*vpert + wpert*wpert);
365 #endif
366  fab_arr(i, j, k, 5) = u_cc_arr(i,j,k) * u_cc_arr(i,j,k); // u*u
367  fab_arr(i, j, k, 6) = u_cc_arr(i,j,k) * v_cc_arr(i,j,k); // u*v
368  fab_arr(i, j, k, 7) = u_cc_arr(i,j,k) * w_cc_arr(i,j,k); // u*w
369  fab_arr(i, j, k, 8) = v_cc_arr(i,j,k) * v_cc_arr(i,j,k); // v*v
370  fab_arr(i, j, k, 9) = v_cc_arr(i,j,k) * w_cc_arr(i,j,k); // v*w
371  fab_arr(i, j, k,10) = w_cc_arr(i,j,k) * w_cc_arr(i,j,k); // w*w
372  fab_arr(i, j, k,11) = u_cc_arr(i,j,k) * theta; // u*th
373  fab_arr(i, j, k,12) = v_cc_arr(i,j,k) * theta; // v*th
374  fab_arr(i, j, k,13) = w_cc_arr(i,j,k) * theta; // w*th
375  fab_arr(i, j, k,14) = theta * theta; // th*th
376 
377  // if the number of fields is changed above, then be sure to update
378  // the following def!
379  Real uiui = fab_arr(i,j,k,5) + fab_arr(i,j,k,8) + fab_arr(i,j,k,10);
380  fab_arr(i, j, k,15) = uiui * u_cc_arr(i,j,k); // (ui*ui)*u
381  fab_arr(i, j, k,16) = uiui * v_cc_arr(i,j,k); // (ui*ui)*v
382  fab_arr(i, j, k,17) = uiui * w_cc_arr(i,j,k); // (ui*ui)*w
383 
384  if (!use_moisture) {
385  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp));
386  p -= p0_arr(i,j,k);
387  fab_arr(i, j, k,18) = p; // p
388  fab_arr(i, j, k,19) = p * u_cc_arr(i,j,k); // p*u
389  fab_arr(i, j, k,20) = p * v_cc_arr(i,j,k); // p*v
390  fab_arr(i, j, k,21) = p * w_cc_arr(i,j,k); // p*w
391  fab_arr(i, j, k,22) = zero; // qv
392  fab_arr(i, j, k,23) = zero; // qc
393  fab_arr(i, j, k,24) = zero; // qr
394  fab_arr(i, j, k,25) = zero; // w*qv
395  fab_arr(i, j, k,26) = zero; // w*qc
396  fab_arr(i, j, k,27) = zero; // w*qr
397  fab_arr(i, j, k,28) = zero; // qi
398  fab_arr(i, j, k,29) = zero; // qs
399  fab_arr(i, j, k,30) = zero; // qg
400  fab_arr(i, j, k,31) = zero; // w*thv
401  }
402  });
403  } // mfi
404 
405  if (use_moisture)
406  {
407  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
408  {
409  const Box& bx = mfi.tilebox();
410  const Array4<Real>& fab_arr = mf_out.array(mfi);
411  const Array4<Real>& cons_arr = mf_cons.array(mfi);
412  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
413  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
414  const Array4<Real>& w_cc_arr = w_cc.array(mfi);
415  const Array4<Real>& p0_arr = p_hse.array(mfi);
416 
417  // The moisture map is the authority on which species this scheme
418  // carries; a slot the scheme allocates but never integrates must not
419  // be reported as a profile of data.
420  int rhoqv_comp = solverChoice.moisture_indices.qv;
421  int rhoqc_comp = solverChoice.moisture_indices.qc;
422  int rhoqr_comp = solverChoice.moisture_indices.qr;
423  int rhoqi_comp = solverChoice.moisture_indices.qi;
424  int rhoqs_comp = solverChoice.moisture_indices.qs;
425  int rhoqg_comp = solverChoice.moisture_indices.qg;
426  AMREX_ALWAYS_ASSERT( (rhoqv_comp > -1) && (rhoqc_comp > -1) );
427 
428  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
429  {
430  Real qv = cons_arr(i,j,k,rhoqv_comp) / cons_arr(i,j,k,Rho_comp);
431  Real qc = cons_arr(i,j,k,rhoqc_comp) / cons_arr(i,j,k,Rho_comp);
432  Real qr = (rhoqr_comp > -1) ? cons_arr(i,j,k,rhoqr_comp) / cons_arr(i,j,k,Rho_comp) :
433  zero;
434  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp), qv);
435 
436  p -= p0_arr(i,j,k);
437  fab_arr(i, j, k,18) = p; // p
438  fab_arr(i, j, k,19) = p * u_cc_arr(i,j,k); // p*u
439  fab_arr(i, j, k,20) = p * v_cc_arr(i,j,k); // p*v
440  fab_arr(i, j, k,21) = p * w_cc_arr(i,j,k); // p*w
441  fab_arr(i, j, k,22) = qv; // qv
442  fab_arr(i, j, k,23) = qc; // qc
443  fab_arr(i, j, k,24) = qr; // qr
444  fab_arr(i, j, k,25) = w_cc_arr(i,j,k) * qv; // w*qv
445  fab_arr(i, j, k,26) = w_cc_arr(i,j,k) * qc; // w*qc
446  fab_arr(i, j, k,27) = w_cc_arr(i,j,k) * qr; // w*qr
447  fab_arr(i, j, k,28) = (rhoqi_comp > -1) ?
448  cons_arr(i,j,k,rhoqi_comp) / cons_arr(i,j,k,Rho_comp) : zero; // qi
449  fab_arr(i, j, k,29) = (rhoqs_comp > -1) ?
450  cons_arr(i,j,k,rhoqs_comp) / cons_arr(i,j,k,Rho_comp) : zero; // qs
451  fab_arr(i, j, k,30) = (rhoqg_comp > -1) ?
452  cons_arr(i,j,k,rhoqg_comp) / cons_arr(i,j,k,Rho_comp) : zero; // qg
453  Real ql = qc + qr;
454  Real theta = cons_arr(i,j,k,RhoTheta_comp) / cons_arr(i,j,k,Rho_comp);
455  Real thv = theta * (one + epsv*qv - ql);
456  fab_arr(i, j, k,31) = w_cc_arr(i,j,k) * thv; // w*thv
457  });
458  } // mfi
459  } // use_moisture
460 
461  h_avg_rho = sumToLine(mf_out, 0,1,domain,zdir);
462  h_avg_th = sumToLine(mf_out, 1,1,domain,zdir);
463  h_avg_ksgs = sumToLine(mf_out, 2,1,domain,zdir);
464  h_avg_Kmv = sumToLine(mf_out, 3,1,domain,zdir);
465  h_avg_Khv = sumToLine(mf_out, 4,1,domain,zdir);
466  h_avg_uu = sumToLine(mf_out, 5,1,domain,zdir);
467  h_avg_uv = sumToLine(mf_out, 6,1,domain,zdir);
468  h_avg_uw = sumToLine(mf_out, 7,1,domain,zdir);
469  h_avg_vv = sumToLine(mf_out, 8,1,domain,zdir);
470  h_avg_vw = sumToLine(mf_out, 9,1,domain,zdir);
471  h_avg_ww = sumToLine(mf_out,10,1,domain,zdir);
472  h_avg_uth = sumToLine(mf_out,11,1,domain,zdir);
473  h_avg_vth = sumToLine(mf_out,12,1,domain,zdir);
474  h_avg_wth = sumToLine(mf_out,13,1,domain,zdir);
475  h_avg_thth = sumToLine(mf_out,14,1,domain,zdir);
476  h_avg_uiuiu = sumToLine(mf_out,15,1,domain,zdir);
477  h_avg_uiuiv = sumToLine(mf_out,16,1,domain,zdir);
478  h_avg_uiuiw = sumToLine(mf_out,17,1,domain,zdir);
479  h_avg_p = sumToLine(mf_out,18,1,domain,zdir);
480  h_avg_pu = sumToLine(mf_out,19,1,domain,zdir);
481  h_avg_pv = sumToLine(mf_out,20,1,domain,zdir);
482  h_avg_pw = sumToLine(mf_out,21,1,domain,zdir);
483  h_avg_qv = sumToLine(mf_out,22,1,domain,zdir);
484  h_avg_qc = sumToLine(mf_out,23,1,domain,zdir);
485  h_avg_qr = sumToLine(mf_out,24,1,domain,zdir);
486  h_avg_wqv = sumToLine(mf_out,25,1,domain,zdir);
487  h_avg_wqc = sumToLine(mf_out,26,1,domain,zdir);
488  h_avg_wqr = sumToLine(mf_out,27,1,domain,zdir);
489  h_avg_qi = sumToLine(mf_out,28,1,domain,zdir);
490  h_avg_qs = sumToLine(mf_out,29,1,domain,zdir);
491  h_avg_qg = sumToLine(mf_out,30,1,domain,zdir);
492  h_avg_wthv = sumToLine(mf_out,31,1,domain,zdir);
493 
494  // Divide by the total number of cells we are averaging over
495  int h_avg_u_size = static_cast<int>(h_avg_u.size());
496  for (int k = 0; k < h_avg_u_size; ++k) {
497  h_avg_rho[k] /= area_z;
498  h_avg_ksgs[k] /= area_z;
499  h_avg_Kmv[k] /= area_z;
500  h_avg_Khv[k] /= area_z;
501  h_avg_th[k] /= area_z;
502  h_avg_thth[k] /= area_z;
503  h_avg_uu[k] /= area_z;
504  h_avg_uv[k] /= area_z;
505  h_avg_uw[k] /= area_z;
506  h_avg_vv[k] /= area_z;
507  h_avg_vw[k] /= area_z;
508  h_avg_ww[k] /= area_z;
509  h_avg_uth[k] /= area_z;
510  h_avg_vth[k] /= area_z;
511  h_avg_wth[k] /= area_z;
512  h_avg_uiuiu[k] /= area_z;
513  h_avg_uiuiv[k] /= area_z;
514  h_avg_uiuiw[k] /= area_z;
515  h_avg_p[k] /= area_z;
516  h_avg_pu[k] /= area_z;
517  h_avg_pv[k] /= area_z;
518  h_avg_pw[k] /= area_z;
519  h_avg_qv[k] /= area_z;
520  h_avg_qc[k] /= area_z;
521  h_avg_qr[k] /= area_z;
522  h_avg_wqv[k] /= area_z;
523  h_avg_wqc[k] /= area_z;
524  h_avg_wqr[k] /= area_z;
525  h_avg_qi[k] /= area_z;
526  h_avg_qs[k] /= area_z;
527  h_avg_qg[k] /= area_z;
528  h_avg_wthv[k] /= area_z;
529  }
530 
531 #if 0
532  // Here we print the integrated total kinetic energy as computed in the 1D profile above
533  Real sum = zero;
534  Real dz = geom[0].ProbHi(2) / static_cast<Real>(h_avg_u_size);
535  for (int k = 0; k < h_avg_u_size; ++k) {
536  sum += h_avg_kturb[k] * h_avg_rho[k] * dz;
537  }
538  amrex::Print() << "ITKE " << time << " " << sum << " using " << h_avg_u_size << " " << dz << std::endl;
539 #endif
540 }
constexpr amrex::Real epsv
Definition: ERF_Constants.H:40
#define RhoKE_comp
Definition: ERF_IndexDefines.H:41
@ Theta_v
Definition: ERF_IndexDefines.H:250
@ Mom_v
Definition: ERF_IndexDefines.H:249
@ theta
Definition: ERF_SLM.H:19
@ qr
Definition: ERF_AdvanceWDM6.cpp:271
int qs
snow
Definition: ERF_DataStruct.H:238
int qi
cloud ice
Definition: ERF_DataStruct.H:236
int qv
water vapor
Definition: ERF_DataStruct.H:234
int qc
cloud liquid water
Definition: ERF_DataStruct.H:235
int qg
graupel
Definition: ERF_DataStruct.H:239
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
timeCurrent simulation time
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_KmvProfile for vertical turbulent viscosity on Host
h_avg_KhvProfile for vertical scalar diffusivity on Host
h_avg_qvProfile for water vapor on Host
h_avg_qcProfile for cloud water on Host
h_avg_qrProfile for rain water on Host
h_avg_wqvProfile for vertical velocity * water vapor on Host
h_avg_wqcProfile for vertical velocity * cloud water on Host
h_avg_wqrProfile for vertical velocity * rain water on Host
h_avg_qiProfile for cloud ice on Host
h_avg_qsProfile for snow on Host
h_avg_qgProfile for graupel 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_vthProfile for y-velocity * potential temperature on Host
h_avg_wthProfile for z-velocity * potential temperature on Host
h_avg_ththProfile for potential temperature squared on Host The definition also carries u_i*u_i velocity triple-product accumulators.
h_avg_kuProfile for resolved x-momentum kinetic-energy flux on Host
h_avg_kvProfile for resolved y-momentum kinetic-energy flux on Host
h_avg_kwProfile for resolved z-momentum kinetic-energy flux 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
h_avg_wthvProfile for vertical velocity * virtual potential temperature on Host
373 {
374  // We assume that this is always called at level 0
375  int lev = 0;
376 
377  bool l_use_kturb = solverChoice.turbChoice[lev].use_kturb;
378  bool l_use_KE = solverChoice.turbChoice[lev].use_tke;
379  // Note: "uiui" == u_i*u_i = u*u + v*v + w*w
380  // This will hold rho, theta, ksgs, Kmh, Kmv, uu, uv, vv, uth, vth,
381  // indices: 0 1 2 3 4 5 6 7 8 9
382  // thth, uiuiu, uiuiv, p, pu, pv, qv, qc, qr, qi, qs, qg
383  // 10 11 12 13 14 15 16 17 18 19 20 21
384  MultiFab mf_out(grids[lev], dmap[lev], 22, 0);
385 
386  // This will hold uw, vw, ww, wth, uiuiw, pw, wqv, wqc, wqr, wthv
387  // indices: 0 1 2 3 4 5 6 7 8 9
388  MultiFab mf_out_stag(convert(grids[lev], IntVect(0,0,1)), dmap[lev], 10, 0);
389 
390  // This is only used to average u and v; w is not averaged to cell centers
391  MultiFab mf_vels(grids[lev], dmap[lev], 2, 0);
392 
393  MultiFab u_cc(mf_vels, make_alias, 0, 1); // u at cell centers
394  MultiFab v_cc(mf_vels, make_alias, 1, 1); // v at cell centers
395  MultiFab w_fc(vars_new[lev][Vars::zvel], make_alias, 0, 1); // w at face centers (staggered)
396 
397  int zdir = 2;
398  auto domain = geom[0].Domain();
399  Box stag_domain = domain;
400  stag_domain.convert(IntVect(0,0,1));
401 
402  int nvars = vars_new[lev][Vars::cons].nComp();
403  MultiFab mf_cons(vars_new[lev][Vars::cons], make_alias, 0, nvars);
404 
405  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
406 
407  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
408  const MultiFab* eta_src = nullptr;
409  const bool have_native_shoc_diagnostics =
410  solverChoice.turbChoice[lev].uses_native_shoc() &&
411  native_shoc_driver[lev] &&
412  native_shoc_driver[lev]->has_native_diagnostics();
413  if (l_use_kturb) {
414  if (have_native_shoc_diagnostics) {
415  eta_src = &native_shoc_driver[lev]->native_diagnostics();
416  } else
417  {
418  eta_src = eddyDiffs_lev[lev].get();
419  }
420  }
421 
422  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
423  {
424  const Box& bx = mfi.tilebox();
425  const Array4<Real>& fab_arr = mf_out.array(mfi);
426  const Array4<Real>& fab_arr_stag = mf_out_stag.array(mfi);
427  const Array4<Real>& u_arr = vars_new[lev][Vars::xvel].array(mfi);
428  const Array4<Real>& v_arr = vars_new[lev][Vars::yvel].array(mfi);
429  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
430  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
431  const Array4<Real>& w_fc_arr = w_fc.array(mfi);
432  const Array4<Real>& cons_arr = mf_cons.array(mfi);
433  const Array4<Real>& p0_arr = p_hse.array(mfi);
434  const Array4<const Real>& eta_arr = (eta_src) ? eta_src->const_array(mfi) :
435  Array4<const Real>{};
436 
437  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
438  {
439  u_cc_arr(i,j,k) = myhalf * (u_arr(i,j,k) + u_arr(i+1,j ,k));
440  v_cc_arr(i,j,k) = myhalf * (v_arr(i,j,k) + v_arr(i ,j+1,k));
441 
442  Real theta = cons_arr(i,j,k,RhoTheta_comp) / cons_arr(i,j,k,Rho_comp);
443  fab_arr(i, j, k, 0) = cons_arr(i,j,k,Rho_comp);
444  fab_arr(i, j, k, 1) = theta;
445  Real ksgs = zero;
446  if (l_use_KE) {
447  ksgs = cons_arr(i,j,k,RhoKE_comp) / cons_arr(i,j,k,Rho_comp);
448  }
449  fab_arr(i, j, k, 2) = ksgs;
450  if (l_use_kturb) {
451  fab_arr(i, j, k, 3) = eta_arr(i,j,k,EddyDiff::Mom_v); // Kmv
452  fab_arr(i, j, k, 4) = eta_arr(i,j,k,EddyDiff::Theta_v); // Khv
453  } else {
454  fab_arr(i, j, k, 3) = zero;
455  fab_arr(i, j, k, 4) = zero;
456  }
457  fab_arr(i, j, k, 5) = u_cc_arr(i,j,k) * u_cc_arr(i,j,k); // u*u
458  fab_arr(i, j, k, 6) = u_cc_arr(i,j,k) * v_cc_arr(i,j,k); // u*v
459  fab_arr(i, j, k, 7) = v_cc_arr(i,j,k) * v_cc_arr(i,j,k); // v*v
460  fab_arr(i, j, k, 8) = u_cc_arr(i,j,k) * theta; // u*th
461  fab_arr(i, j, k, 9) = v_cc_arr(i,j,k) * theta; // v*th
462  fab_arr(i, j, k,10) = theta * theta; // th*th
463 
464  Real wcc = myhalf * (w_fc_arr(i,j,k) + w_fc_arr(i,j,k+1));
465 
466  // if the number of fields is changed above, then be sure to update
467  // the following def!
468  Real uiui = fab_arr(i,j,k,5) + fab_arr(i,j,k,7) + wcc*wcc;
469  fab_arr(i, j, k,11) = uiui * u_cc_arr(i,j,k); // (ui*ui)*u
470  fab_arr(i, j, k,12) = uiui * v_cc_arr(i,j,k); // (ui*ui)*v
471 
472  if (!use_moisture) {
473  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp));
474  p -= p0_arr(i,j,k);
475  fab_arr(i, j, k,13) = p; // p
476  fab_arr(i, j, k,14) = p * u_cc_arr(i,j,k); // p*u
477  fab_arr(i, j, k,15) = p * v_cc_arr(i,j,k); // p*v
478  fab_arr(i, j, k,16) = zero; // qv
479  fab_arr(i, j, k,17) = zero; // qc
480  fab_arr(i, j, k,18) = zero; // qr
481  fab_arr(i, j, k,19) = zero; // qi
482  fab_arr(i, j, k,20) = zero; // qs
483  fab_arr(i, j, k,21) = zero; // qg
484  }
485  });
486 
487  const Box& zbx = mfi.tilebox(IntVect(0,0,1));
488  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
489  {
490  // average to z faces (first to cell centers, then in z)
491  Real uface = fourth * ( u_arr(i ,j,k) + u_arr(i ,j,k-1)
492  + u_arr(i+1,j,k) + u_arr(i+1,j,k-1));
493  Real vface = fourth * ( v_arr(i,j ,k) + v_arr(i,j ,k-1)
494  + v_arr(i,j+1,k) + v_arr(i,j+1,k-1));
495  Real theta0 = cons_arr(i,j,k ,RhoTheta_comp) / cons_arr(i,j,k ,Rho_comp);
496  Real theta1 = cons_arr(i,j,k-1,RhoTheta_comp) / cons_arr(i,j,k-1,Rho_comp);
497  Real thface = myhalf*(theta0 + theta1);
498  fab_arr_stag(i,j,k,0) = uface * w_fc_arr(i,j,k); // u*w
499  fab_arr_stag(i,j,k,1) = vface * w_fc_arr(i,j,k); // v*w
500  fab_arr_stag(i,j,k,2) = w_fc_arr(i,j,k) * w_fc_arr(i,j,k); // w*w
501  fab_arr_stag(i,j,k,3) = thface * w_fc_arr(i,j,k); // th*w
502  Real uiui = uface*uface + vface*vface + fab_arr_stag(i,j,k,2);
503  fab_arr_stag(i,j,k,4) = uiui * w_fc_arr(i,j,k); // (ui*ui)*w
504  if (!use_moisture) {
505  Real p0 = getPgivenRTh(cons_arr(i, j, k , RhoTheta_comp)) - p0_arr(i,j,k );
506  Real p1 = getPgivenRTh(cons_arr(i, j, k-1, RhoTheta_comp)) - p0_arr(i,j,k-1);
507  Real pface = myhalf * (p0 + p1);
508  fab_arr_stag(i,j,k,5) = pface * w_fc_arr(i,j,k); // p*w
509  fab_arr_stag(i,j,k,6) = zero; // w*qv
510  fab_arr_stag(i,j,k,7) = zero; // w*qc
511  fab_arr_stag(i,j,k,8) = zero; // w*qr
512  fab_arr_stag(i,j,k,9) = zero; // w*thv
513  }
514  });
515 
516  } // mfi
517 
518  if (use_moisture)
519  {
520  for ( MFIter mfi(mf_cons,TilingIfNotGPU()); mfi.isValid(); ++mfi)
521  {
522  const Box& bx = mfi.tilebox();
523  const Array4<Real>& fab_arr = mf_out.array(mfi);
524  const Array4<Real>& fab_arr_stag = mf_out_stag.array(mfi);
525  const Array4<Real>& cons_arr = mf_cons.array(mfi);
526  const Array4<Real>& u_cc_arr = u_cc.array(mfi);
527  const Array4<Real>& v_cc_arr = v_cc.array(mfi);
528  const Array4<Real>& w_fc_arr = w_fc.array(mfi);
529  const Array4<Real>& p0_arr = p_hse.array(mfi);
530 
531  // The moisture map is the authority on which species this scheme
532  // carries; a slot the scheme allocates but never integrates must not
533  // be reported as a profile of data.
534  int rhoqv_comp = solverChoice.moisture_indices.qv;
535  int rhoqc_comp = solverChoice.moisture_indices.qc;
536  int rhoqr_comp = solverChoice.moisture_indices.qr;
537  int rhoqi_comp = solverChoice.moisture_indices.qi;
538  int rhoqs_comp = solverChoice.moisture_indices.qs;
539  int rhoqg_comp = solverChoice.moisture_indices.qg;
540  AMREX_ALWAYS_ASSERT( (rhoqv_comp > -1) && (rhoqc_comp > -1) );
541 
542  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
543  {
544  Real qv = cons_arr(i,j,k,rhoqv_comp) / cons_arr(i,j,k,Rho_comp);
545  Real qc = cons_arr(i,j,k,rhoqc_comp) / cons_arr(i,j,k,Rho_comp);
546  Real qr = (rhoqr_comp > -1) ? cons_arr(i,j,k,rhoqr_comp) / cons_arr(i,j,k,Rho_comp) :
547  zero;
548  Real p = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp), qv);
549 
550  p -= p0_arr(i,j,k);
551  fab_arr(i, j, k,13) = p; // p
552  fab_arr(i, j, k,14) = p * u_cc_arr(i,j,k); // p*u
553  fab_arr(i, j, k,15) = p * v_cc_arr(i,j,k); // p*v
554  fab_arr(i, j, k,16) = qv; // qv
555  fab_arr(i, j, k,17) = qc; // qc
556  fab_arr(i, j, k,18) = qr; // qr
557  fab_arr(i, j, k,19) = (rhoqi_comp > -1) ?
558  cons_arr(i,j,k,rhoqi_comp) / cons_arr(i,j,k,Rho_comp) : zero; // qi
559  fab_arr(i, j, k,20) = (rhoqs_comp > -1) ?
560  cons_arr(i,j,k,rhoqs_comp) / cons_arr(i,j,k,Rho_comp) : zero; // qs
561  fab_arr(i, j, k,21) = (rhoqg_comp > -1) ?
562  cons_arr(i,j,k,rhoqg_comp) / cons_arr(i,j,k,Rho_comp) : zero; // qg
563  });
564 
565  const Box& zbx = mfi.tilebox(IntVect(0,0,1));
566  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
567  {
568  Real qv0 = cons_arr(i,j,k ,rhoqv_comp) / cons_arr(i,j,k ,Rho_comp);
569  Real qv1 = cons_arr(i,j,k-1,rhoqv_comp) / cons_arr(i,j,k-1,Rho_comp);
570  Real qc0 = cons_arr(i,j,k ,rhoqc_comp) / cons_arr(i,j,k ,Rho_comp);
571  Real qc1 = cons_arr(i,j,k-1,rhoqc_comp) / cons_arr(i,j,k-1,Rho_comp);
572  Real qr0 = (rhoqr_comp > -1) ? cons_arr(i,j,k ,rhoqr_comp) / cons_arr(i,j,k ,Rho_comp) :
573  zero;
574  Real qr1 = (rhoqr_comp > -1) ? cons_arr(i,j,k-1,rhoqr_comp) / cons_arr(i,j,k-1,Rho_comp) :
575  zero;
576  Real qvface = myhalf * (qv0 + qv1);
577  Real qcface = myhalf * (qc0 + qc1);
578  Real qrface = myhalf * (qr0 + qr1);
579 
580  Real p0 = getPgivenRTh(cons_arr(i, j, k , RhoTheta_comp), qv0) - p0_arr(i,j,k );
581  Real p1 = getPgivenRTh(cons_arr(i, j, k-1, RhoTheta_comp), qv1) - p0_arr(i,j,k-1);
582  Real pface = myhalf * (p0 + p1);
583 
584  Real theta0 = cons_arr(i,j,k ,RhoTheta_comp) / cons_arr(i,j,k ,Rho_comp);
585  Real theta1 = cons_arr(i,j,k-1,RhoTheta_comp) / cons_arr(i,j,k-1,Rho_comp);
586  Real thface = myhalf*(theta0 + theta1);
587  Real ql = qcface + qrface;
588  Real thv = thface * (one + epsv*qvface - ql);
589 
590  fab_arr_stag(i,j,k,5) = pface * w_fc_arr(i,j,k); // p*w
591  fab_arr_stag(i,j,k,6) = qvface * w_fc_arr(i,j,k); // w*qv
592  fab_arr_stag(i,j,k,7) = qcface * w_fc_arr(i,j,k); // w*qc
593  fab_arr_stag(i,j,k,8) = qrface * w_fc_arr(i,j,k); // w*qr
594  fab_arr_stag(i,j,k,9) = thv * w_fc_arr(i,j,k); // w*thv
595  });
596  } // mfi
597  } // use_moisture
598 
599  // Sum in the horizontal plane
600  h_avg_u = sumToLine(u_cc,0,1, domain,zdir);
601  h_avg_v = sumToLine(v_cc,0,1, domain,zdir);
602  h_avg_w = sumToLine(w_fc,0,1,stag_domain,zdir);
603 
604  h_avg_rho = sumToLine(mf_out, 0,1,domain,zdir);
605  h_avg_th = sumToLine(mf_out, 1,1,domain,zdir);
606  h_avg_ksgs = sumToLine(mf_out, 2,1,domain,zdir);
607  h_avg_Kmv = sumToLine(mf_out, 3,1,domain,zdir);
608  h_avg_Khv = sumToLine(mf_out, 4,1,domain,zdir);
609  h_avg_uu = sumToLine(mf_out, 5,1,domain,zdir);
610  h_avg_uv = sumToLine(mf_out, 6,1,domain,zdir);
611  h_avg_vv = sumToLine(mf_out, 7,1,domain,zdir);
612  h_avg_uth = sumToLine(mf_out, 8,1,domain,zdir);
613  h_avg_vth = sumToLine(mf_out, 9,1,domain,zdir);
614  h_avg_thth = sumToLine(mf_out,10,1,domain,zdir);
615  h_avg_uiuiu = sumToLine(mf_out,11,1,domain,zdir);
616  h_avg_uiuiv = sumToLine(mf_out,12,1,domain,zdir);
617  h_avg_p = sumToLine(mf_out,13,1,domain,zdir);
618  h_avg_pu = sumToLine(mf_out,14,1,domain,zdir);
619  h_avg_pv = sumToLine(mf_out,15,1,domain,zdir);
620  h_avg_qv = sumToLine(mf_out,16,1,domain,zdir);
621  h_avg_qc = sumToLine(mf_out,17,1,domain,zdir);
622  h_avg_qr = sumToLine(mf_out,18,1,domain,zdir);
623  h_avg_qi = sumToLine(mf_out,19,1,domain,zdir);
624  h_avg_qs = sumToLine(mf_out,20,1,domain,zdir);
625  h_avg_qg = sumToLine(mf_out,21,1,domain,zdir);
626 
627  h_avg_uw = sumToLine(mf_out_stag,0,1,stag_domain,zdir);
628  h_avg_vw = sumToLine(mf_out_stag,1,1,stag_domain,zdir);
629  h_avg_ww = sumToLine(mf_out_stag,2,1,stag_domain,zdir);
630  h_avg_wth = sumToLine(mf_out_stag,3,1,stag_domain,zdir);
631  h_avg_uiuiw = sumToLine(mf_out_stag,4,1,stag_domain,zdir);
632  h_avg_pw = sumToLine(mf_out_stag,5,1,stag_domain,zdir);
633  h_avg_wqv = sumToLine(mf_out_stag,6,1,stag_domain,zdir);
634  h_avg_wqc = sumToLine(mf_out_stag,7,1,stag_domain,zdir);
635  h_avg_wqr = sumToLine(mf_out_stag,8,1,stag_domain,zdir);
636  h_avg_wthv = sumToLine(mf_out_stag,9,1,stag_domain,zdir);
637 
638  // Divide by the total number of cells we are averaging over
639  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
640  int unstag_size = h_avg_w.size() - 1; // _un_staggered heights
641  for (int k = 0; k < unstag_size; ++k) {
642  h_avg_u[k] /= area_z;
643  h_avg_v[k] /= area_z;
644  h_avg_rho[k] /= area_z;
645  h_avg_ksgs[k] /= area_z;
646  h_avg_Kmv[k] /= area_z;
647  h_avg_Khv[k] /= area_z;
648  h_avg_th[k] /= area_z;
649  h_avg_thth[k] /= area_z;
650  h_avg_uu[k] /= area_z;
651  h_avg_uv[k] /= area_z;
652  h_avg_vv[k] /= area_z;
653  h_avg_uth[k] /= area_z;
654  h_avg_vth[k] /= area_z;
655  h_avg_uiuiu[k] /= area_z;
656  h_avg_uiuiv[k] /= area_z;
657  h_avg_p[k] /= area_z;
658  h_avg_pu[k] /= area_z;
659  h_avg_pv[k] /= area_z;
660  h_avg_qv[k] /= area_z;
661  h_avg_qc[k] /= area_z;
662  h_avg_qr[k] /= area_z;
663  h_avg_qi[k] /= area_z;
664  h_avg_qs[k] /= area_z;
665  h_avg_qg[k] /= area_z;
666  }
667 
668  for (int k = 0; k < unstag_size+1; ++k) { // staggered heights
669  h_avg_w[k] /= area_z;
670  h_avg_uw[k] /= area_z;
671  h_avg_vw[k] /= area_z;
672  h_avg_ww[k] /= area_z;
673  h_avg_wth[k] /= area_z;
674  h_avg_uiuiw[k] /= area_z;
675  h_avg_pw[k] /= area_z;
676  h_avg_wqv[k] /= area_z;
677  h_avg_wqc[k] /= area_z;
678  h_avg_wqr[k] /= area_z;
679  h_avg_wthv[k] /= area_z;
680  }
681 }
real(kind=kind_phys), save qc1
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save qc0
Definition: ERF_module_mp_wdm6.F90:100
Here is the call graph for this function:

◆ derive_forcing_profiles_stag()

void ERF::derive_forcing_profiles_stag ( amrex::Gpu::HostVector< amrex::Real > &  h_avg_ttend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qtend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_wsub,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_thtend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qhtend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tvtend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qvtend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qcvtend,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_tnudge,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_qnudge,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_unudge,
amrex::Gpu::HostVector< amrex::Real > &  h_avg_vnudge 
)
809 {
810  // We assume that this is always called at level 0
811  int lev = 0;
812  int zdir = 2;
813  auto domain = geom[0].Domain();
814  int nz = domain.length(2);
815 
816  // Sum in the horizontal plane
818  h_avg_ttend = sumToLine(*lsf_data[lev], 0,1,domain,zdir);
819  h_avg_qtend = sumToLine(*lsf_data[lev], 1,1,domain,zdir);
820  h_avg_wsub = sumToLine(*lsf_data[lev], 2,1,domain,zdir);
821 
822  // horizontal and vertical tendencies:
823  h_avg_thtend = sumToLine(*lsf_data[lev], 3,1,domain,zdir);
824  h_avg_qhtend = sumToLine(*lsf_data[lev], 4,1,domain,zdir);
825  h_avg_tvtend = sumToLine(*lsf_data[lev], 5,1,domain,zdir);
826  h_avg_qvtend = sumToLine(*lsf_data[lev], 6,1,domain,zdir);
827  h_avg_qcvtend = sumToLine(*lsf_data[lev], 7,1,domain,zdir);
828  } else {
829  h_avg_ttend = Gpu::HostVector<Real>(nz, zero);
830  h_avg_qtend = Gpu::HostVector<Real>(nz, zero);
831  h_avg_wsub = Gpu::HostVector<Real>(nz, zero);
832  h_avg_thtend = Gpu::HostVector<Real>(nz, zero);
833  h_avg_qhtend = Gpu::HostVector<Real>(nz, zero);
834  h_avg_tvtend = Gpu::HostVector<Real>(nz, zero);
835  h_avg_qvtend = Gpu::HostVector<Real>(nz, zero);
836  h_avg_qcvtend = Gpu::HostVector<Real>(nz, zero);
837  }
838 
840  h_avg_tnudge = sumToLine(*nudge_data[lev], 0,1,domain,zdir);
841  h_avg_qnudge = sumToLine(*nudge_data[lev], 1,1,domain,zdir);
842  h_avg_unudge = sumToLine(*nudge_data[lev], 2,1,domain,zdir);
843  h_avg_vnudge = sumToLine(*nudge_data[lev], 3,1,domain,zdir);
844  } else {
845  h_avg_tnudge = Gpu::HostVector<Real>(nz, zero);
846  h_avg_qnudge = Gpu::HostVector<Real>(nz, zero);
847  h_avg_unudge = Gpu::HostVector<Real>(nz, zero);
848  h_avg_vnudge = Gpu::HostVector<Real>(nz, zero);
849  }
850 
851  // Divide by the total number of cells we are averaging over
852  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
853  for (int k = 0; k < nz; ++k) {
855  h_avg_ttend[k] /= area_z;
856  h_avg_qtend[k] /= area_z;
857  h_avg_wsub[k] /= area_z;
858 
859  h_avg_thtend[k] /= area_z;
860  h_avg_qhtend[k] /= area_z;
861  h_avg_tvtend[k] /= area_z;
862  h_avg_qvtend[k] /= area_z;
863  h_avg_qcvtend[k] /= area_z;
864  }
865 
867  h_avg_tnudge[k] /= area_z;
868  h_avg_qnudge[k] /= area_z;
869  h_avg_unudge[k] /= area_z;
870  h_avg_vnudge[k] /= area_z;
871  }
872  }
873 }

◆ 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 
)
548 {
549  int lev = 0;
550 
551  // This will hold the stress tensor components
552  MultiFab mf_out(grids[lev], dmap[lev], 10, 0);
553 
554  MultiFab mf_rho(vars_new[lev][Vars::cons], make_alias, 0, 1);
555 
556  bool l_use_moist = ( solverChoice.moisture_type != MoistureType::None );
557 
558  for ( MFIter mfi(mf_out,TilingIfNotGPU()); mfi.isValid(); ++mfi)
559  {
560  const Box& bx = mfi.tilebox();
561  const Array4<Real>& fab_arr = mf_out.array(mfi);
562 
563  const Array4<const Real>& rho_arr = mf_rho.const_array(mfi);
564 
565  // NOTE: These are from the last RK stage...
566  const Array4<const Real>& tau11_arr = Tau[lev][TauType::tau11]->const_array(mfi);
567  const Array4<const Real>& tau12_arr = Tau[lev][TauType::tau12]->const_array(mfi);
568  const Array4<const Real>& tau13_arr = Tau[lev][TauType::tau13]->const_array(mfi);
569  const Array4<const Real>& tau22_arr = Tau[lev][TauType::tau22]->const_array(mfi);
570  const Array4<const Real>& tau23_arr = Tau[lev][TauType::tau23]->const_array(mfi);
571  const Array4<const Real>& tau33_arr = Tau[lev][TauType::tau33]->const_array(mfi);
572 
573  // These should be re-calculated during ERF_slow_rhs_post
574  // -- just vertical SFS kinematic heat flux for now
575  //const Array4<const Real>& hfx1_arr = SFS_hfx1_lev[lev]->const_array(mfi);
576  //const Array4<const Real>& hfx2_arr = SFS_hfx2_lev[lev]->const_array(mfi);
577  const Array4<const Real>& hfx3_arr = SFS_hfx3_lev[lev]->const_array(mfi);
578  const Array4<const Real>& q1fx3_arr = (l_use_moist) ? SFS_q1fx3_lev[lev]->const_array(mfi) :
579  Array4<const Real>{};
580  const Array4<const Real>& q2fx3_arr = (l_use_moist) ? SFS_q2fx3_lev[lev]->const_array(mfi) :
581  Array4<const Real>{};
582  const Array4<const Real>& diss_arr = SFS_diss_lev[lev]->const_array(mfi);
583 
584  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
585  {
586  // rho averaging should follow Diffusion/ERF_ComputeStress_*.cpp
587  fab_arr(i, j, k, 0) = tau11_arr(i,j,k) / rho_arr(i,j,k);
588  fab_arr(i, j, k, 1) = ( tau12_arr(i,j ,k) + tau12_arr(i+1,j ,k)
589  + tau12_arr(i,j+1,k) + tau12_arr(i+1,j+1,k) )
590  / ( rho_arr(i,j ,k) + rho_arr(i+1,j ,k)
591  + rho_arr(i,j+1,k) + rho_arr(i+1,j+1,k) );
592  fab_arr(i, j, k, 2) = ( tau13_arr(i,j,k ) + tau13_arr(i+1,j,k )
593  + tau13_arr(i,j,k+1) + tau13_arr(i+1,j,k+1) )
594  / ( rho_arr(i,j,k ) + rho_arr(i+1,j,k )
595  + rho_arr(i,j,k+1) + rho_arr(i+1,j,k+1) );
596  fab_arr(i, j, k, 3) = tau22_arr(i,j,k) / rho_arr(i,j,k);
597  fab_arr(i, j, k, 4) = ( tau23_arr(i,j,k ) + tau23_arr(i,j+1,k )
598  + tau23_arr(i,j,k+1) + tau23_arr(i,j+1,k+1) )
599  / ( rho_arr(i,j,k ) + rho_arr(i,j+1,k )
600  + rho_arr(i,j,k+1) + rho_arr(i,j+1,k+1) );
601  fab_arr(i, j, k, 5) = tau33_arr(i,j,k) / rho_arr(i,j,k);
602  fab_arr(i, j, k, 6) = myhalf * ( hfx3_arr(i,j,k) + hfx3_arr(i,j,k+1) ) / rho_arr(i,j,k);
603  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;
604  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;
605  fab_arr(i, j, k, 9) = diss_arr(i,j,k) / rho_arr(i,j,k);
606  });
607  }
608 
609  int zdir = 2;
610  auto domain = geom[0].Domain();
611 
612  h_avg_tau11 = sumToLine(mf_out,0,1,domain,zdir);
613  h_avg_tau12 = sumToLine(mf_out,1,1,domain,zdir);
614  h_avg_tau13 = sumToLine(mf_out,2,1,domain,zdir);
615  h_avg_tau22 = sumToLine(mf_out,3,1,domain,zdir);
616  h_avg_tau23 = sumToLine(mf_out,4,1,domain,zdir);
617  h_avg_tau33 = sumToLine(mf_out,5,1,domain,zdir);
618  h_avg_hfx3 = sumToLine(mf_out,6,1,domain,zdir);
619  h_avg_q1fx3 = sumToLine(mf_out,7,1,domain,zdir);
620  h_avg_q2fx3 = sumToLine(mf_out,8,1,domain,zdir);
621  h_avg_diss = sumToLine(mf_out,9,1,domain,zdir);
622 
623  int ht_size = h_avg_tau11.size();
624 
625  // Divide by the total number of cells we are averaging over
626  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
627  for (int k = 0; k < ht_size; ++k) {
628  h_avg_tau11[k] /= area_z;
629  h_avg_tau12[k] /= area_z;
630  h_avg_tau13[k] /= area_z;
631  h_avg_tau22[k] /= area_z;
632  h_avg_tau23[k] /= area_z;
633  h_avg_tau33[k] /= area_z;
634  h_avg_hfx3[k] /= area_z;
635  h_avg_q1fx3[k] /= area_z;
636  h_avg_q2fx3[k] /= area_z;
637  h_avg_diss[k] /= area_z;
638  }
639 }
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 
)
689 {
690  int lev = 0;
691 
692  // This will hold the stress tensor components
693  MultiFab mf_out(grids[lev], dmap[lev], 10, 0);
694 
695  // This will hold Tau13 and Tau23
696  MultiFab mf_out_stag(convert(grids[lev], IntVect(0,0,1)), dmap[lev], 5, 0);
697 
698  MultiFab mf_rho(vars_new[lev][Vars::cons], make_alias, 0, 1);
699 
700  bool l_use_moist = ( solverChoice.moisture_type != MoistureType::None );
701 
702  for ( MFIter mfi(mf_out,TilingIfNotGPU()); mfi.isValid(); ++mfi)
703  {
704  const Box& bx = mfi.tilebox();
705  const Array4<Real>& fab_arr = mf_out.array(mfi);
706  const Array4<Real>& fab_arr_stag = mf_out_stag.array(mfi);
707 
708  const Array4<const Real>& rho_arr = mf_rho.const_array(mfi);
709 
710  // NOTE: These are from the last RK stage...
711  const Array4<const Real>& tau11_arr = Tau[lev][TauType::tau11]->const_array(mfi);
712  const Array4<const Real>& tau12_arr = Tau[lev][TauType::tau12]->const_array(mfi);
713  const Array4<const Real>& tau13_arr = Tau[lev][TauType::tau13]->const_array(mfi);
714  const Array4<const Real>& tau22_arr = Tau[lev][TauType::tau22]->const_array(mfi);
715  const Array4<const Real>& tau23_arr = Tau[lev][TauType::tau23]->const_array(mfi);
716  const Array4<const Real>& tau33_arr = Tau[lev][TauType::tau33]->const_array(mfi);
717 
718  // These should be re-calculated during ERF_slow_rhs_post
719  // -- just vertical SFS kinematic heat flux for now
720  //const Array4<const Real>& hfx1_arr = SFS_hfx1_lev[lev]->const_array(mfi);
721  //const Array4<const Real>& hfx2_arr = SFS_hfx2_lev[lev]->const_array(mfi);
722  const Array4<const Real>& hfx3_arr = SFS_hfx3_lev[lev]->const_array(mfi);
723  const Array4<const Real>& q1fx3_arr = (l_use_moist) ? SFS_q1fx3_lev[lev]->const_array(mfi) :
724  Array4<const Real>{};
725  const Array4<const Real>& q2fx3_arr = (l_use_moist) ? SFS_q2fx3_lev[lev]->const_array(mfi) :
726  Array4<const Real>{};
727  const Array4<const Real>& diss_arr = SFS_diss_lev[lev]->const_array(mfi);
728 
729  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
730  {
731  // rho averaging should follow Diffusion/ERF_ComputeStress_*.cpp
732  fab_arr(i, j, k, 0) = tau11_arr(i,j,k) / rho_arr(i,j,k);
733  fab_arr(i, j, k, 1) = ( tau12_arr(i,j ,k) + tau12_arr(i+1,j ,k)
734  + tau12_arr(i,j+1,k) + tau12_arr(i+1,j+1,k) )
735  / ( rho_arr(i,j ,k) + rho_arr(i+1,j ,k)
736  + rho_arr(i,j+1,k) + rho_arr(i+1,j+1,k) );
737  fab_arr(i, j, k, 3) = tau22_arr(i,j,k) / rho_arr(i,j,k);
738  fab_arr(i, j, k, 5) = tau33_arr(i,j,k) / rho_arr(i,j,k);
739  fab_arr(i, j, k, 9) = diss_arr(i,j,k) / rho_arr(i,j,k);
740  });
741 
742  const Box& zbx = mfi.tilebox(IntVect(0,0,1));
743  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
744  {
745  Real rho_face = myhalf * (rho_arr(i,j,k-1) + rho_arr(i,j,k));
746  // average from edge to face center
747  fab_arr_stag(i,j,k,0) = myhalf*(tau13_arr(i,j,k) + tau13_arr(i+1,j ,k)) / rho_face;
748  fab_arr_stag(i,j,k,1) = myhalf*(tau23_arr(i,j,k) + tau23_arr(i ,j+1,k)) / rho_face;
749 
750  fab_arr_stag(i,j,k,2) = hfx3_arr(i,j,k) / rho_face;
751  fab_arr_stag(i,j,k,3) = (l_use_moist) ? q1fx3_arr(i,j,k) / rho_face : zero;
752  fab_arr_stag(i,j,k,4) = (l_use_moist) ? q2fx3_arr(i,j,k) / rho_face : zero;
753  });
754  }
755 
756  int zdir = 2;
757  auto domain = geom[0].Domain();
758  Box stag_domain = domain;
759  stag_domain.convert(IntVect(0,0,1));
760 
761  h_avg_tau11 = sumToLine(mf_out,0,1,domain,zdir);
762  h_avg_tau12 = sumToLine(mf_out,1,1,domain,zdir);
763 // h_avg_tau13 = sumToLine(mf_out,2,1,domain,zdir);
764  h_avg_tau22 = sumToLine(mf_out,3,1,domain,zdir);
765 // h_avg_tau23 = sumToLine(mf_out,4,1,domain,zdir);
766  h_avg_tau33 = sumToLine(mf_out,5,1,domain,zdir);
767 // h_avg_hfx3 = sumToLine(mf_out,6,1,domain,zdir);
768 // h_avg_q1fx3 = sumToLine(mf_out,7,1,domain,zdir);
769 // h_avg_q2fx3 = sumToLine(mf_out,8,1,domain,zdir);
770  h_avg_diss = sumToLine(mf_out,9,1,domain,zdir);
771 
772  h_avg_tau13 = sumToLine(mf_out_stag,0,1,stag_domain,zdir);
773  h_avg_tau23 = sumToLine(mf_out_stag,1,1,stag_domain,zdir);
774  h_avg_hfx3 = sumToLine(mf_out_stag,2,1,stag_domain,zdir);
775  h_avg_q1fx3 = sumToLine(mf_out_stag,3,1,stag_domain,zdir);
776  h_avg_q2fx3 = sumToLine(mf_out_stag,4,1,stag_domain,zdir);
777 
778  int ht_size = h_avg_tau11.size(); // _un_staggered
779 
780  // Divide by the total number of cells we are averaging over
781  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
782  for (int k = 0; k < ht_size; ++k) {
783  h_avg_tau11[k] /= area_z;
784  h_avg_tau12[k] /= area_z;
785  h_avg_tau13[k] /= area_z;
786  h_avg_tau22[k] /= area_z;
787  h_avg_tau23[k] /= area_z;
788  h_avg_tau33[k] /= area_z;
789  h_avg_hfx3[k] /= area_z;
790  h_avg_q1fx3[k] /= area_z;
791  h_avg_q2fx3[k] /= area_z;
792  h_avg_diss[k] /= area_z;
793  }
794  // staggered heights
795  h_avg_tau13[ht_size] /= area_z;
796  h_avg_tau23[ht_size] /= area_z;
797  h_avg_hfx3[ht_size] /= area_z;
798  h_avg_q1fx3[ht_size] /= area_z;
799  h_avg_q2fx3[ht_size] /= area_z;
800 }
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
2173  {
2174  AMREX_ASSERT(lev >= 0 && lev < eb.size() && eb[lev] != nullptr);
2175  AMREX_ASSERT(eb[lev]->get_const_factory() != nullptr);
2176  return *(eb[lev]->get_const_factory());
2177  }
amrex::Vector< std::unique_ptr< eb_ > > eb
Definition: ERF.H:2159

◆ 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
[out]thMultiFab storing base state potential temperature
[out]qvMultiFab storing base state water vapor mixing ratio
[in]z_ccPointer to MultiFab storing cell centered z-coordinates
231 {
232  Real l_gravity = solverChoice.gravity;
233  bool l_use_terrain = (solverChoice.mesh_type != MeshType::ConstantDz);
234 
235  const auto geomdata = geom[lev].data();
236  const Real dz = geomdata.CellSize(2);
237 
238  //
239  // A box stacked on another box of this level continues from the pressure (and density) that
240  // box reached; see erf_init_dens_hse_dry. The boxes are integrated in bands of equal lowest
241  // index, bottom up, and before each band the cells just below it are filled from the bands
242  // already done. With a single band nothing is filled.
243  //
244  const Vector<int> bands = column_bands(dens.boxArray());
245 
246  for (const int klo_band : bands)
247  {
248  if (klo_band != bands[0]) {
249  fill_below_band(dens, 0, 1, klo_band, IntVect(1,1,0), geom[lev]);
250  fill_below_band(pres, 0, 1, klo_band, IntVect(1,1,0), geom[lev]);
251  }
252 
253  for ( MFIter mfi(dens, TileNoZ()); mfi.isValid(); ++mfi )
254  {
255  // Create a flat box with same horizontal extent but only one cell in vertical
256  const Box& tbz = mfi.nodaltilebox(2);
257  int klo = tbz.smallEnd(2);
258  int khi = tbz.bigEnd(2);
259 
260  if (klo != klo_band) { continue; }
261 
262  // Note we only grow by 1 because that is how big z_cc is.
263  Box b2d = tbz; // Copy constructor
264  b2d.grow(0,1);
265  b2d.grow(1,1);
266  b2d.setRange(2,0);
267 
268  // Intersect this box with the domain
269  Box zdomain = convert(geom[lev].Domain(),tbz.ixType());
270  b2d &= zdomain;
271 
272  // We integrate to the first cell (and below) by using rho in this cell
273  // If gravity == 0 this is constant pressure
274  // If gravity != 0, hence this is a wall, this gives gp0 = dens[0] * gravity
275  // (dens_hse*gravity would also be dens[0]*gravity because we use foextrap for rho at k = -1)
276  // Note ng_pres_hse = 1
277 
278  // We start by assuming pressure on the ground is p_0 (in ERF_Constants.H)
279  // Note that gravity is positive
280 
281  Array4<Real> rho_arr = dens.array(mfi);
282  Array4<Real> pres_arr = pres.array(mfi);
283  Array4<Real> pi_arr = pi.array(mfi);
284  Array4<Real> th_arr = theta.array(mfi);
285  Array4<Real> zcc_arr;
286  if (l_use_terrain) {
287  zcc_arr = z_cc->array(mfi);
288  }
289 
290  const Real rdOcp = solverChoice.rdOcp;
291 
292  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int)
293  {
294  // Set value at surface from Newton iteration for rho
295  if (klo == 0)
296  {
297  // Physical height of the terrain at cell center
298  Real hz;
299  if (l_use_terrain) {
300  hz = zcc_arr(i,j,klo);
301  } else {
302  hz = myhalf*dz;
303  }
304 
305  pres_arr(i,j,klo) = p_0 - hz * rho_arr(i,j,klo) * l_gravity;
306  pi_arr(i,j,klo) = getExnergivenP(pres_arr(i,j,klo), rdOcp);
307  th_arr(i,j,klo) = getRhoThetagivenP(pres_arr(i,j,klo)) / rho_arr(i,j,klo);
308 
309  //
310  // Set ghost cell with dz and rho at boundary
311  // (We will set the rest of the ghost cells in the boundary condition routine)
312  //
313  pres_arr(i,j,klo-1) = p_0 + hz * rho_arr(i,j,klo) * l_gravity;
314  pi_arr(i,j,klo-1) = getExnergivenP(pres_arr(i,j,klo-1), rdOcp);
315  th_arr(i,j,klo-1) = getRhoThetagivenP(pres_arr(i,j,klo-1)) / rho_arr(i,j,klo-1);
316 
317  } else {
318 
319  // If klo > 0 we use the value of pres_arr(i,j,klo-1): the pressure the box of this
320  // level below reached or, where there is no such box, the one interpolated from
321  // the coarser level.
322  Real dz_loc;
323  if (l_use_terrain) {
324  dz_loc = (zcc_arr(i,j,klo) - zcc_arr(i,j,klo-1));
325  } else {
326  dz_loc = dz;
327  }
328 
329  Real dens_interp = myhalf*(rho_arr(i,j,klo) + rho_arr(i,j,klo-1));
330  pres_arr(i,j,klo) = pres_arr(i,j,klo-1) - dz_loc * dens_interp * l_gravity;
331 
332  pi_arr(i,j,klo ) = getExnergivenP(pres_arr(i,j,klo ), rdOcp);
333  th_arr(i,j,klo ) = getRhoThetagivenP(pres_arr(i,j,klo )) / rho_arr(i,j,klo );
334 
335  pi_arr(i,j,klo-1) = getExnergivenP(pres_arr(i,j,klo-1), rdOcp);
336  th_arr(i,j,klo-1) = getRhoThetagivenP(pres_arr(i,j,klo-1)) / rho_arr(i,j,klo-1);
337  }
338 
339  Real dens_interp;
340  if (l_use_terrain) {
341  for (int k = klo+1; k <= khi; k++) {
342  Real dz_loc = (zcc_arr(i,j,k) - zcc_arr(i,j,k-1));
343  dens_interp = myhalf*(rho_arr(i,j,k) + rho_arr(i,j,k-1));
344  pres_arr(i,j,k) = pres_arr(i,j,k-1) - dz_loc * dens_interp * l_gravity;
345  pi_arr(i,j,k) = getExnergivenP(pres_arr(i,j,k), rdOcp);
346  th_arr(i,j,k) = getRhoThetagivenP(pres_arr(i,j,k)) / rho_arr(i,j,k);
347  }
348  } else {
349  for (int k = klo+1; k <= khi; k++) {
350  dens_interp = myhalf*(rho_arr(i,j,k) + rho_arr(i,j,k-1));
351  pres_arr(i,j,k) = pres_arr(i,j,k-1) - dz * dens_interp * l_gravity;
352  pi_arr(i,j,k) = getExnergivenP(pres_arr(i,j,k), rdOcp);
353  th_arr(i,j,k) = getRhoThetagivenP(pres_arr(i,j,k)) / rho_arr(i,j,k);
354  }
355  }
356  });
357 
358  } // mfi
359  } // band
360 
361  dens.FillBoundary(geom[lev].periodicity());
362  pres.FillBoundary(geom[lev].periodicity());
363  pi.FillBoundary(geom[lev].periodicity());
364  theta.FillBoundary(geom[lev].periodicity());
365  qv.FillBoundary(geom[lev].periodicity());
366 }
Vector< int > column_bands(const BoxArray &ba)
Definition: ERF_ColumnBands.cpp:13
void fill_below_band(MultiFab &mf, int icomp, int ncomp, int klo_band, const IntVect &lateral_ng, const Geometry &geom)
Definition: ERF_ColumnBands.cpp:23
constexpr amrex::Real p_0
Definition: ERF_Constants.H:53
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_ABL.H:72
@ pres
Definition: ERF_Kessler.H:28
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_wdm6.F90:61
amrex::Real rdOcp
Ratio of dry-air gas constant to c_p.
Definition: ERF_DataStruct.H:2062
Here is the call graph for this function:

◆ ERF_shared()

void ERF::ERF_shared ( )
50 {
51  // Seeding lives here so every constructor gets it
52  int fix_random_seed = 0;
53  long random_seed = -1;
54  {
55  ParmParse pp("erf");
56  pp.queryAdd("fix_random_seed", fix_random_seed);
57  pp.queryAdd("random_seed", random_seed);
58  }
59  // Note that the value of 1024UL is not significant -- the point here is just to set the
60  // same seed for all MPI processes for the purpose of regression testing
61  if (fix_random_seed) {
62  Print() << "Fixing the random seed" << std::endl;
63  InitRandom(1024UL, ParallelDescriptor::NProcs(), 1024UL);
64  } else if (random_seed >= 0) {
65  // User-supplied seed: vary the random sampling per run (e.g. across ensemble
66  // realizations), still offset by rank so the ranks draw independent streams.
67  Print() << "Using user random seed " << random_seed << std::endl;
68  auto s = static_cast<unsigned long>(random_seed);
69  InitRandom(s + static_cast<unsigned long>(ParallelDescriptor::MyProc()) + 1UL,
70  ParallelDescriptor::NProcs(), s * 1234567UL + 12345UL);
71  }
72 
73  if (ParallelDescriptor::IOProcessor()) {
74  const char* erf_hash = buildInfoGetGitHash(1);
75  const char* amrex_hash = buildInfoGetGitHash(2);
76  const char* buildgithash = buildInfoGetBuildGitHash();
77  const char* buildgitname = buildInfoGetBuildGitName();
78 
79  if (strlen(erf_hash) > 0) {
80  Print() << "\n"
81  << "ERF git hash: " << erf_hash << "\n";
82  }
83  if (strlen(amrex_hash) > 0) {
84  Print() << "AMReX git hash: " << amrex_hash << "\n";
85  }
86  if (strlen(buildgithash) > 0) {
87  Print() << buildgitname << " git hash: " << buildgithash << "\n";
88  }
89 
90  Print() << "\n";
91  }
92 
93  int nlevs_max = max_level + 1;
94 
95 #ifdef ERF_USE_WINDFARM
96  Nturb.resize(nlevs_max);
97  vars_windfarm.resize(nlevs_max);
98  SMark.resize(nlevs_max);
99 #endif
100 
101  qheating_rates.resize(nlevs_max);
102  rad_fluxes.resize(nlevs_max);
103  two_stream_rad.resize(nlevs_max);
104 
105  // NOTE: size lsm before readparams (chooses the model at all levels)
106  lsm.ReSize(nlevs_max);
107  lsm_data.resize(nlevs_max);
108  lsm_flux.resize(nlevs_max);
109 
110  nudge_data.resize(nlevs_max);
111  lsf_data.resize(nlevs_max);
112 
113  rhotheta_src.resize(nlevs_max);
114  rhoqt_src.resize(nlevs_max);
115 
116  // NOTE: size canopy model before readparams (if file exists, we construct)
117  m_forest_drag.resize(nlevs_max);
118  for (int lev = 0; lev <= max_level; ++lev) { m_forest_drag[lev] = nullptr;}
119 
120  // Surface layer object for each possible face
121  m_SurfaceLayer.resize(AMREX_SPACEDIM*2);
122 
123  ReadParameters();
124  // Create one invocation identity after inputs are available and before
125  // InitData can read restart metadata or write an output on restart.
127  initializeMicrophysics(nlevs_max);
128 
129 #ifdef ERF_USE_WINDFARM
130  initializeWindFarm(nlevs_max);
131 #endif
132 
133 #ifdef ERF_USE_EAMXX_SHOC
134  eamxx_shoc_interface.resize(nlevs_max);
135  for (int lev = 0; lev <= max_level; ++lev) {
136  if (solverChoice.turbChoice[lev].uses_eamxx_shoc()) {
137  eamxx_shoc_interface[lev] = std::make_unique<SHOCInterface>(lev, solverChoice);
138  }
139  }
140 #endif
141 
142  native_shoc_driver.resize(nlevs_max);
143  for (int lev = 0; lev <= max_level; ++lev) {
144  if (solverChoice.turbChoice[lev].uses_native_shoc()) {
145  native_shoc_driver[lev] = std::make_unique<ShocDriver>(lev, solverChoice);
146  }
147  }
148 
149  rad.resize(nlevs_max);
150  for (int lev = 0; lev <= max_level; ++lev) {
151  if (solverChoice.rad_type == RadiationType::RRTMGP) {
152 #ifdef ERF_USE_RRTMGP
153  rad[lev] = std::make_unique<Radiation>(lev, solverChoice);
154  // pass radiation datalog frequency to model - RRTMGP needs to know when to save data for profiles
155  rad[lev]->setDataLogFrequency(rad_datalog_int);
156 #endif
157  } else if (solverChoice.rad_type == RadiationType::Simple) {
158  rad[lev] = std::make_unique<RadiationSimple>(lev, solverChoice);
159  rad[lev]->setDataLogFrequency(rad_datalog_int);
160  } else if (solverChoice.rad_type == RadiationType::TwoStream) {
161  // Runs through TwoStreamRadiation (two_stream_rad), not IRadiation.
162  } else if (solverChoice.rad_type != RadiationType::None) {
163  Abort("Don't know this radiation model!");
164  }
165  }
166  // NOTE: these must come after initializeMicrophysics() -- the conserved-state
167  // layout they select against is owned by the microphysics interface.
168  const std::string& pv3d_1 = "plot_vars_1" ; setPlotVariables(pv3d_1,plot3d_var_names_1);
169  const std::string& pv3d_2 = "plot_vars_2" ; setPlotVariables(pv3d_2,plot3d_var_names_2);
170  setSubVolVariables("subvol_sampling_vars",subvol3d_var_names);
171 
172  // This is only used when we have mesh_type == MeshType::StretchedDz
173  stretched_dz_h.resize(nlevs_max);
174  stretched_dz_d.resize(nlevs_max);
175 
176  // Initialize staggered vertical levels for grid stretching or terrain, and
177  // to simplify Rayleigh damping layer calculations.
178  zlevels_stag.resize(max_level+1);
182  geom,
183  refRatio(),
186  solverChoice.dz0);
187 
188  if (SolverChoice::mesh_type == MeshType::StretchedDz ||
189  SolverChoice::mesh_type == MeshType::VariableDz) {
190  int nz = geom[0].Domain().length(2) + 1; // staggered
191  if (std::fabs(zlevels_stag[0][nz-1]-geom[0].ProbHi(2)) > Real(1.0e-4)) {
192  Print() << "Note: prob_hi[2]=" << geom[0].ProbHi(2)
193  << " does not match highest requested z level " << zlevels_stag[0][nz-1]
194  << std::endl;
195  }
196  if (std::fabs(zlevels_stag[0][0]-geom[0].ProbLo(2)) > Real(1.0e-4)) {
197  Print() << "Note: prob_lo[2]=" << geom[0].ProbLo(2)
198  << " does not match lowest requested level " << zlevels_stag[0][0]
199  << std::endl;
200  }
201 
202  // Redefine the problem domain here?
203  }
204 
205  // Get lo/hi indices for massflux calc
207  if (solverChoice.mesh_type == MeshType::ConstantDz) {
210  const Real massflux_zlo = solverChoice.const_massflux_layer_lo - geom[0].ProbLo(2);
211  const Real massflux_zhi = solverChoice.const_massflux_layer_hi - geom[0].ProbLo(2);
212  const Real dz = geom[0].CellSize(2);
213  if (zlo_unset) {
214  solverChoice.massflux_klo = geom[0].Domain().smallEnd(2);
215  } else {
216  solverChoice.massflux_klo = static_cast<int>(std::ceil(massflux_zlo / dz - myhalf));
217  }
218  if (zhi_unset) {
219  solverChoice.massflux_khi = geom[0].Domain().bigEnd(2);
220  } else {
221  solverChoice.massflux_khi = static_cast<int>(std::floor(massflux_zhi / dz - myhalf));
222  }
223  } else if (solverChoice.mesh_type == MeshType::StretchedDz) {
224  const Real massflux_zlo = solverChoice.const_massflux_layer_lo;
225  const Real massflux_zhi = solverChoice.const_massflux_layer_hi;
226  solverChoice.massflux_klo = geom[0].Domain().smallEnd(2);
227  solverChoice.massflux_khi = geom[0].Domain().bigEnd(2) + 1;
228  for (int k=0; k <= geom[0].Domain().bigEnd(2)+1; ++k) {
229  if (zlevels_stag[0][k] <= massflux_zlo) solverChoice.massflux_klo = k;
230  if (zlevels_stag[0][k] <= massflux_zhi) solverChoice.massflux_khi = k;
231  }
232  } else { // solverChoice.mesh_type == MeshType::VariableDz
233  Error("Const massflux with variable dz not supported -- planar averages are on k rather than constant-z planes");
234  }
235 
236  Print() << "Constant mass flux based on k in ["
237  << solverChoice.massflux_klo << ", " << solverChoice.massflux_khi << "]" << std::endl;
238  }
239 
240 #ifdef ERF_REMORA_FORCE_PROBINIT_LINK
241  extern void erf_probinit_link_anchor_func () noexcept;
242  erf_probinit_link_anchor_func();
243 #endif
244  prob = amrex_probinit(geom[0].ProbLo(),geom[0].ProbHi());
245 
246  ParmParse pp_erf("erf");
247  std::string prob_name;
248  pp_erf.queryAdd("prob_name", prob_name);
249  const std::string prob_name_ci = amrex::toLower(prob_name);
250  if (prob_name_ci == "cloud chamber" || prob_name_ci == "cloudchamber") {
252  geom[0].ProbLo(), geom[0].ProbHi());
253  const amrex::GpuArray<Real, AMREX_SPACEDIM> cloud_chamber_dx = {
254  geom[0].CellSize(0), geom[0].CellSize(1), geom[0].CellSize(2)};
256  cloud_chamber_config.wall_boundary(), cloud_chamber_dx);
257  }
258  {
259  int budget_interval = 0;
260  pp_erf.queryAdd("cloud_chamber_budget_interval", budget_interval);
261  if (budget_interval > 0) {
264  (solverChoice.moisture_type != MoistureType::None &&
265  solverChoice.moisture_type != MoistureType::SatAdj)) {
266  Error("Cloud Chamber: cloud_chamber_budget_interval requires physical_temperature_rh with no moisture or SatAdj");
267  }
268  cloud_chamber_budget = std::make_unique<CloudChamberBudget>(budget_interval);
269  }
270  }
271 
272  // Geometry on all levels has been defined already.
273 
274  // No valid BoxArray and DistributionMapping have been defined.
275  // But the arrays for them have been resized.
276 
277  t_new.resize(nlevs_max, zero);
278  t_old.resize(nlevs_max, -bogus_large_value);
279  dt.resize(nlevs_max, std::min(static_cast<double>(bogus_large_value),dt_max_initial));
280  dt_mri_ratio.resize(nlevs_max, 1);
281 
282  vars_new.resize(nlevs_max);
283  vars_old.resize(nlevs_max);
284  gradp.resize(nlevs_max);
285 
286  // We resize this regardless in order to pass it without error
287  pp_inc.resize(nlevs_max);
288 
289  // Used in the fast substepping only
290  lagged_delta_rt.resize(nlevs_max);
291  avg_xmom.resize(nlevs_max);
292  avg_ymom.resize(nlevs_max);
293  avg_zmom.resize(nlevs_max);
294 
295  rU_new.resize(nlevs_max);
296  rV_new.resize(nlevs_max);
297  rW_new.resize(nlevs_max);
298 
299  rU_old.resize(nlevs_max);
300  rV_old.resize(nlevs_max);
301  rW_old.resize(nlevs_max);
302 
303  // xmom_crse_rhs.resize(nlevs_max);
304  // ymom_crse_rhs.resize(nlevs_max);
305  zmom_crse_rhs.resize(nlevs_max);
306 
307  for (int lev = 0; lev < nlevs_max; ++lev) {
308  vars_new[lev].resize(Vars::NumTypes);
309  vars_old[lev].resize(Vars::NumTypes);
310  gradp[lev].resize(AMREX_SPACEDIM);
311  }
312 
313  // Time integrator
314  mri_integrator_mem.resize(nlevs_max);
315 
316  // Physical boundary conditions
317  physbcs_cons.resize(nlevs_max);
318  physbcs_u.resize(nlevs_max);
319  physbcs_v.resize(nlevs_max);
320  physbcs_w.resize(nlevs_max);
321  physbcs_base.resize(nlevs_max);
322 
323  // Planes to hold Dirichlet values at boundaries
324  xvel_bc_data.resize(nlevs_max);
325  yvel_bc_data.resize(nlevs_max);
326  zvel_bc_data.resize(nlevs_max);
327  th_bc_data.resize(nlevs_max);
328 
329  advflux_reg.resize(nlevs_max);
330 
331  // Stresses
332  Tau.resize(nlevs_max);
333  Tau_corr.resize(nlevs_max);
334  SFS_hfx1_lev.resize(nlevs_max); SFS_hfx2_lev.resize(nlevs_max); SFS_hfx3_lev.resize(nlevs_max);
335  SFS_diss_lev.resize(nlevs_max);
336  SFS_q1fx1_lev.resize(nlevs_max); SFS_q1fx2_lev.resize(nlevs_max); SFS_q1fx3_lev.resize(nlevs_max);
337  SFS_q2fx3_lev.resize(nlevs_max);
338  eddyDiffs_lev.resize(nlevs_max);
339  SmnSmn_lev.resize(nlevs_max);
340  Tau_EB.resize(nlevs_max);
341  hfx3_EB.resize(nlevs_max);
342 
343  // Sea surface temps
344  sst_lev.resize(nlevs_max);
345  tsk_lev.resize(nlevs_max);
346  lmask_lev.resize(nlevs_max);
347 
348  // Land and soil grid type and urban fractions
349  land_type_lev.resize(nlevs_max);
350  soil_type_lev.resize(nlevs_max);
351  urb_frac_lev.resize(nlevs_max);
352 
353  // Metric terms
354  z_phys_nd.resize(nlevs_max);
355  z_phys_cc.resize(nlevs_max);
356  detJ_cc.resize(nlevs_max);
357  ax.resize(nlevs_max);
358  ay.resize(nlevs_max);
359  az.resize(nlevs_max);
360 
361  z_phys_nd_new.resize(nlevs_max);
362  detJ_cc_new.resize(nlevs_max);
363 
364  z_phys_nd_src.resize(nlevs_max);
365  z_phys_cc_src.resize(nlevs_max);
366  detJ_cc_src.resize(nlevs_max);
367  ax_src.resize(nlevs_max);
368  ay_src.resize(nlevs_max);
369  az_src.resize(nlevs_max);
370 
371  z_t_rk.resize(nlevs_max);
372 
373  terrain_blanking.resize(nlevs_max);
374  terrain_blanking_xface.resize(nlevs_max);
375  terrain_blanking_yface.resize(nlevs_max);
376  terrain_blanking_zface.resize(nlevs_max);
377 
378  // Wall distance
379  walldist.resize(nlevs_max);
380 
381  // BoxArrays to make MultiFabs needed to convert WRFBdy data
382  ba1d.resize(nlevs_max);
383  ba2d.resize(nlevs_max);
384  column_kextent.resize(nlevs_max);
385 
386  // MultiFabs needed to convert WRFBdy data
387  mf_PSFC.resize(nlevs_max);
388 
389  // Map factors
390  mapfac.resize(nlevs_max);
391 
392  // Fine mask
393  fine_mask.resize(nlevs_max);
394 
395  // Thin immersed body
396  xflux_imask.resize(nlevs_max);
397  yflux_imask.resize(nlevs_max);
398  zflux_imask.resize(nlevs_max);
399  //overset_imask.resize(nlevs_max);
400  thin_xforce.resize(nlevs_max);
401  thin_yforce.resize(nlevs_max);
402  thin_zforce.resize(nlevs_max);
403 
404  // Base state
405  base_state.resize(nlevs_max);
406  base_state_new.resize(nlevs_max);
407 
408  // Wave coupling data
409  Hwave.resize(nlevs_max);
410  Lwave.resize(nlevs_max);
411  for (int lev = 0; lev < max_level; ++lev)
412  {
413  Hwave[lev] = nullptr;
414  Lwave[lev] = nullptr;
415  }
416  Hwave_onegrid.resize(nlevs_max);
417  Lwave_onegrid.resize(nlevs_max);
418  for (int lev = 0; lev < max_level; ++lev)
419  {
420  Hwave_onegrid[lev] = nullptr;
421  Lwave_onegrid[lev] = nullptr;
422  }
423 
424  // Theta prim for MOST
425  Theta_prim.resize(nlevs_max);
426 
427  // Qv prim for MOST
428  Qv_prim.resize(nlevs_max);
429 
430  // Qr prim for MOST
431  Qr_prim.resize(nlevs_max);
432 
433  // Time averaged velocity field
434  vel_t_avg.resize(nlevs_max);
435  t_avg_cnt.resize(nlevs_max);
436 
437  // Interval mean variables
438  interval_means.resize(nlevs_max);
439  t_mean_cnt.resize(nlevs_max);
441 
442  // Size lat long arrays and default to null pointers
443  lat_m.resize(nlevs_max);
444  lon_m.resize(nlevs_max);
445  for (int lev = 0; lev < max_level; ++lev) {
446  lat_m[lev] = nullptr;
447  lon_m[lev] = nullptr;
448  }
449 
450  // Variable coriolis
451  sinPhi_m.resize(nlevs_max);
452  cosPhi_m.resize(nlevs_max);
453  for (int lev = 0; lev < max_level; ++lev) {
454  sinPhi_m[lev] = nullptr;
455  cosPhi_m[lev] = nullptr;
456  }
457 
458  // Rayleigh damping
459  h_rayleigh_ptrs.resize(nlevs_max);
460  d_rayleigh_ptrs.resize(nlevs_max);
461  h_sinesq_ptrs.resize(nlevs_max);
462  d_sinesq_ptrs.resize(nlevs_max);
463  h_sinesq_stag_ptrs.resize(nlevs_max);
464  d_sinesq_stag_ptrs.resize(nlevs_max);
465 
466  // Planar averages for immersed forcing
467  r_plane_avg.resize(nlevs_max);
468  t_plane_avg.resize(nlevs_max);
469 
470  // Initialize tagging criteria for mesh refinement
472 
473  for (int lev = 0; lev < max_level; ++lev)
474  {
475  Print() << "Refinement ratio at level " << lev+1 << " set to be " <<
476  ref_ratio[lev][0] << " " << ref_ratio[lev][1] << " " << ref_ratio[lev][2] << std::endl;
477  }
478 
479  // We will create each of these in MakeNewLevelFromScratch
480  eb.resize(max_level+1);
481  for (int lev = 0; lev < max_level + 1; lev++){
482  eb[lev] = std::make_unique<eb_>();
483  }
484 
485  // Construct EB geometry based on terrain and buildings configuration
486  initializeEB();
487 
488  forecast_state_1.resize(nlevs_max);
489  forecast_state_2.resize(nlevs_max);
490  forecast_state_interp.resize(nlevs_max);
491 
492  surface_state_1.resize(nlevs_max);
493  surface_state_2.resize(nlevs_max);
494  surface_state_interp.resize(nlevs_max);
495 }
long random_seed
Definition: ERF_InitCustomPert_ABL.H:6
int fix_random_seed
Definition: ERF_InitCustomPert_ABL.H:5
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:23
std::unique_ptr< ProblemBase > amrex_probinit(const amrex_real *problo, const amrex_real *probhi) AMREX_ATTRIBUTE_WEAK
amrex::Vector< std::string > subvol3d_var_names
Definition: ERF.H:1561
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Hwave_onegrid
Definition: ERF.H:1419
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_yforce
Definition: ERF.H:1461
void initializeEB()
Definition: ERF_EBInit.cpp:23
void setPlotVariables(const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile.cpp:19
amrex::Vector< amrex::Vector< amrex::MultiFab > > gradp
Definition: ERF.H:1229
void ReadParameters()
Definition: ERF.cpp:2603
amrex::Vector< std::unique_ptr< amrex::MultiFab > > mf_PSFC
Definition: ERF.H:1770
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd_src
Definition: ERF.H:1379
amrex::Vector< amrex::MultiFab > base_state_new
Definition: ERF.H:1414
amrex::Vector< std::unique_ptr< amrex::MultiFab > > az
Definition: ERF.H:1377
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking_xface
Definition: ERF.H:1397
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_nd_new
Definition: ERF.H:1386
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_zforce
Definition: ERF.H:1462
amrex::Vector< std::string > plot3d_var_names_2
Definition: ERF.H:1564
amrex::Vector< std::unique_ptr< amrex::MultiFab > > thin_xforce
Definition: ERF.H:1460
erf_cloud_chamber::Config cloud_chamber_config
Definition: ERF.H:1278
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > th_bc_data
Definition: ERF.H:1164
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking_zface
Definition: ERF.H:1399
amrex::Vector< amrex::MultiFab > surface_state_1
Definition: ERF.H:297
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_t_rk
Definition: ERF.H:1389
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Lwave_onegrid
Definition: ERF.H:1420
amrex::Vector< amrex::Vector< amrex::Real > > h_sinesq_ptrs
Definition: ERF.H:1819
erf_provenance::ExecutionProvenance execution_provenance
Definition: ERF.H:1505
void setSubVolVariables(const std::string &pp_subvol_var_names, amrex::Vector< std::string > &subvol_var_names)
Definition: ERF_WriteSubvolume.cpp:13
amrex::Vector< std::unique_ptr< amrex::MultiFab > > fine_mask
Definition: ERF.H:1408
amrex::Vector< std::unique_ptr< ForestDrag > > m_forest_drag
Definition: ERF.H:1847
amrex::Vector< amrex::BoxArray > ba1d
Definition: ERF.H:1736
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > xvel_bc_data
Definition: ERF.H:1161
int rad_datalog_int
Definition: ERF.H:1329
amrex::Vector< amrex::MultiFab > surface_state_2
Definition: ERF.H:298
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ax
Definition: ERF.H:1375
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc_src
Definition: ERF.H:1381
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ay_src
Definition: ERF.H:1383
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > yflux_imask
Definition: ERF.H:1455
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_flux
Definition: ERF.H:1298
amrex::Vector< std::string > plot3d_var_names_1
Definition: ERF.H:1563
void refinement_criteria_setup()
Definition: ERF_Tagging.cpp:502
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > Tau_corr
Definition: ERF.H:1341
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ax_src
Definition: ERF.H:1382
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > urb_frac_lev
Definition: ERF.H:1353
amrex::Vector< std::unique_ptr< amrex::MultiFab > > z_phys_cc_src
Definition: ERF.H:1380
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_2
Definition: ERF.H:295
static double dt_max_initial
Definition: ERF.H:1512
amrex::Vector< std::unique_ptr< amrex::MultiFab > > terrain_blanking_yface
Definition: ERF.H:1398
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > soil_type_lev
Definition: ERF.H:1352
amrex::Vector< amrex::Vector< amrex::Real > > zlevels_stag
Definition: ERF.H:1368
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_data
Definition: ERF.H:1296
amrex::Vector< amrex::Vector< amrex::Real > > stretched_dz_h
Definition: ERF.H:1410
amrex::Vector< std::unique_ptr< amrex::MultiFab > > az_src
Definition: ERF.H:1384
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Lwave
Definition: ERF.H:1418
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab > > > land_type_lev
Definition: ERF.H:1351
amrex::Vector< amrex::Vector< amrex::MultiFab > > forecast_state_1
Definition: ERF.H:294
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > zflux_imask
Definition: ERF.H:1456
amrex::Vector< std::unique_ptr< amrex::MultiFab > > ay
Definition: ERF.H:1376
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > zvel_bc_data
Definition: ERF.H:1163
amrex::Vector< amrex::Vector< amrex::Real > > h_sinesq_stag_ptrs
Definition: ERF.H:1820
amrex::Vector< std::unique_ptr< amrex::MultiFab > > detJ_cc_new
Definition: ERF.H:1387
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > yvel_bc_data
Definition: ERF.H:1162
amrex::Vector< amrex::MultiFab > surface_state_interp
Definition: ERF.H:299
amrex::Vector< amrex::Vector< amrex::Vector< amrex::Real > > > h_rayleigh_ptrs
Definition: ERF.H:1815
amrex::Vector< std::unique_ptr< amrex::MultiFab > > Hwave
Definition: ERF.H:1417
amrex::Vector< amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > > Tau_EB
Definition: ERF.H:1363
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > xflux_imask
Definition: ERF.H:1454
void initializeMicrophysics(const int &)
Definition: ERF.cpp:2308
void ReSize(const int &nlev)
Definition: ERF_LandSurface.H:28
void resize(int nlevs_max)
Size the per-level containers; no allocation happens here.
Definition: ERF_TwoStreamRadiation.cpp:205
const char * buildInfoGetGitHash(int i)
Config parse_config(const amrex::Real *problo, const amrex::Real *probhi)
Definition: ERF_CloudChamber.H:699
void validate_wall_roughness_geometry(const erf_wall_thermodynamics::Boundary &walls, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dx)
Definition: ERF_CloudChamber.H:691
ExecutionProvenance initialize_execution_provenance()
Definition: ERF_Provenance.cpp:562
amrex::Real const_massflux_v
Target constant mass flux in the y direction.
Definition: ERF_DataStruct.H:2305
amrex::Real const_massflux_layer_lo
Lower height of the constant-mass-flux forcing layer.
Definition: ERF_DataStruct.H:2307
amrex::Real const_massflux_u
Target constant mass flux in the x direction.
Definition: ERF_DataStruct.H:2304
amrex::Real const_massflux_layer_hi
Upper height of the constant-mass-flux forcing layer.
Definition: ERF_DataStruct.H:2308
amrex::Real dz0
Initial vertical grid spacing for stretched-grid setup.
Definition: ERF_DataStruct.H:2067
int massflux_khi
Upper vertical index for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:2310
amrex::Real zsurf
Nominal surface height for stretched-grid setup.
Definition: ERF_DataStruct.H:2066
int massflux_klo
Lower vertical index for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:2309
amrex::Real grid_stretching_ratio
Vertical grid stretching ratio.
Definition: ERF_DataStruct.H:2065
erf_wall_thermodynamics::Boundary wall_boundary() const noexcept
Collect thermodynamic boundary conditions for all chamber walls.
Definition: ERF_CloudChamber.H:61
bool active
Definition: ERF_CloudChamber.H:41
bool physical_initialization
Definition: ERF_CloudChamber.H:43
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)
35 {
36  const int clearval = TagBox::CLEAR;
37  const int tagval = TagBox::SET;
38 
39 #ifdef ERF_USE_NETCDF
40  if ((solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid)) {
41  int ratio;
42  Box subdomain;
43 
44  // This is the number of boxes that may have already been defined in the refinement_criteria_setup routine.
45  // If nb == 0 then no boxes have been specified in the inputs file, and we will use the boxes given in wrfinput_d*
46  // If nb > 0 then boxes have been specified in the inputs file, and we will use the specified boxes as long
47  // as we can ensure that they are contained inside the boxes given in wrfinput_d*
48  int nb_prespecified = num_boxes_at_level[levc+1];
49 
50  if (!nc_init_file[levc+1].empty())
51  {
52  double levc_start_time = read_start_time_from_wrfinput(levc , nc_init_file[levc ][0]);
53  if (solverChoice.init_type == InitType::WRFInput) {
54  amrex::Print() << " WRFInput time at level " << levc << " is " << levc_start_time << std::endl;
55  } else if (solverChoice.init_type == InitType::Metgrid) {
56  amrex::Print() << " met_em time at level " << levc << " is " << levc_start_time << std::endl;
57  }
58 
59  for (int isub = 0; isub < nc_init_file[levc+1].size(); isub++) {
60  if (!have_read_nc_init_file[levc+1][isub])
61  {
62  double levf_start_time = read_start_time_from_wrfinput(levc+1, nc_init_file[levc+1][isub]);
63  if (solverChoice.init_type == InitType::WRFInput) {
64  amrex::Print() << " WRFInput start_time at level " << levc+1 << " is " << levf_start_time << std::endl;
65  } else if (solverChoice.init_type == InitType::Metgrid) {
66  amrex::Print() << " met_em start time at level " << levc+1 << " is " << levf_start_time << std::endl;
67  }
68 
69  // We assume there is only one subdomain at levc; otherwise we don't know
70  // which one is the parent of the fine region we are trying to create
71  AMREX_ALWAYS_ASSERT(subdomains[levc].size() == 1);
72 
73  if ( levf_start_time <= (levc_start_time + t_new[levc]) ) {
74  if (solverChoice.init_type == InitType::WRFInput) {
75  amrex::Print() << " WRFInput file to read: " << nc_init_file[levc+1][isub] << std::endl;
76  subdomain = read_subdomain_from_wrfinput(levc, nc_init_file[levc+1][isub], ratio);
77  amrex::Print() << " WRFInput subdomain " << isub << " at level " << levc+1 << " is " << subdomain << std::endl;
78  } else if (solverChoice.init_type == InitType::Metgrid) {
79  amrex::Print() << "met_em file to read: " << nc_init_file[levc+1][0] << std::endl;
80  const Box& domain = geom[levc].Domain();
81  int klo = domain.smallEnd(2);
82  int khi = domain.bigEnd(2);
83  subdomain = read_subdomain_from_metgrid(levc, nc_init_file[levc+1][0], ratio, klo, khi);
84  amrex::Print() << " met_em subdomain at level " << levc+1 << " is " << subdomain << std::endl;
85  }
86 
87  if ( (ratio != ref_ratio[levc][0]) || (ratio != ref_ratio[levc][1]) ) {
88  amrex::Print() << "File " << nc_init_file[levc+1][0] << " has refinement ratio = " << ratio << std::endl;
89  amrex::Print() << "The inputs file has refinement ratio = " << ref_ratio[levc] << std::endl;
90  amrex::Abort("These must be the same in the horizontal -- please edit your inputs file and try again.");
91  }
92 
93  if (solverChoice.init_type == InitType::WRFInput) {
94  //
95  // A WRF nest is refined in the horizontal only: wrfinput_d02 carries
96  // exactly the eta levels of wrfinput_d01. The z extent just read is
97  // therefore already in the PARENT's vertical index space, so only the
98  // horizontal directions are coarsened here; any vertical refinement is
99  // applied when the box is refined below. Clip that extent to the
100  // parent's domain in case the file carries more layers than this run
101  // was given cells in z.
102  //
103  subdomain.setSmall(2, amrex::max(subdomain.smallEnd(2),
104  geom[levc].Domain().smallEnd(2)));
105  subdomain.setBig (2, amrex::min(subdomain.bigEnd(2),
106  geom[levc].Domain().bigEnd(2)));
107  subdomain.coarsen(IntVect(ref_ratio[levc][0],ref_ratio[levc][1],1));
108  } else {
109  subdomain.coarsen(ref_ratio[levc]);
110  }
111 
112  // Recall we asserted that there is only one box at level levc
113  Box coarser_level(subdomains[levc][0].minimalBox());
114  subdomain.shift(coarser_level.smallEnd());
115 
116  if (verbose > 0) {
117  amrex::Print() << " Crse version of subdomain available for tagging is" << subdomain << std::endl;
118  }
119 
120  Box new_fine(subdomain);
121  if (solverChoice.init_type == InitType::WRFInput) {
122  // The horizontal ratio comes from the file (and was just checked
123  // against the inputs file); the vertical one is whatever the inputs
124  // file asked for, since the file itself has nothing to say about it.
125  new_fine.refine(IntVect(ratio,ratio,ref_ratio[levc][2]));
126  } else if (solverChoice.init_type == InitType::Metgrid) {
127  new_fine.refine(ref_ratio[levc]);
128  }
129  if (nb_prespecified == 0) {
130  num_boxes_at_level[levc+1] += 1;
131  boxes_at_level[levc+1].push_back(new_fine);
132  } else {
133  if (!new_fine.contains(boxes_at_level[levc+1][isub])) {
134  amrex::Print() << "\n";
135  amrex::Print() << "Box available in wrfinputs file " << new_fine << std::endl;
136  amrex::Print() << "Box requested for refinement in inputs file " << boxes_at_level[levc+1][isub] << std::endl;
137  amrex::Abort("Specified boxes must be contained within boxes specified in wrfinput at this level");
138  }
139  }
140 
141  Box coarsened_bx(boxes_at_level[levc+1][isub]); coarsened_bx.coarsen(ref_ratio[levc]);
142 
143  for (MFIter mfi(tags); mfi.isValid(); ++mfi)
144  {
145  auto tag_arr = tags.array(mfi); // Get device-accessible array
146 
147  Box bx = mfi.validbox() & coarsened_bx;
148 
149  if (!bx.isEmpty()) {
150  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
151  tag_arr(i,j,k) = TagBox::SET;
152  });
153  }
154  }
155  } // time is right
156  } else {
157  // Re-tag this region
158  for (MFIter mfi(tags); mfi.isValid(); ++mfi)
159  {
160  auto tag_arr = tags.array(mfi); // Get device-accessible array
161 
162  Box existing_bx_coarsened(boxes_at_level[levc+1][isub]);
163  existing_bx_coarsened.coarsen(ref_ratio[levc]);
164 
165  Box bx = mfi.validbox(); bx &= existing_bx_coarsened;
166 
167  if (!bx.isEmpty()) {
168  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
169  tag_arr(i,j,k) = TagBox::SET;
170  });
171  }
172  }
173  } // has file been read?
174  } // isub
175  return;
176  } // file not empty
177  }
178 #endif
179 
180  //
181  // Make sure the ghost cells of the level we are tagging at are filled
182  // in case we take differences that require them
183  // NOTE: We are Fillpatching only the cell-centered variables here
184  //
185  MultiFab& S_new = vars_new[levc][Vars::cons];
186  MultiFab& U_new = vars_new[levc][Vars::xvel];
187  MultiFab& V_new = vars_new[levc][Vars::yvel];
188  MultiFab& W_new = vars_new[levc][Vars::zvel];
189  //
190  if (levc == 0) {
191  FillPatchCrseLevel(levc, time, {&S_new, &U_new, &V_new, &W_new});
192  } else {
193  FillPatchFineLevel(levc, time, {&S_new, &U_new, &V_new, &W_new},
194  {&S_new, &rU_new[levc], &rV_new[levc], &rW_new[levc]},
195  base_state[levc], base_state[levc],
196  false, true);
197  }
198 
199  for (int t=0; t < ref_tags.size(); ++t)
200  {
201  //
202  // This mf must have ghost cells because we may take differences between adjacent values
203  //
204  std::unique_ptr<MultiFab> mf = std::make_unique<MultiFab>(grids[levc], dmap[levc], 1, 1);
205  mf->setVal(0.0);
206 
207  RealBox real_box = ref_tags[t].GetInfo().m_realbox;
208  if (real_box.ok()) {
209  // Use the indicator name stored when this tag was created; the tags do not
210  // correspond one-to-one with the entries of erf.refinement_indicators.
211  int lev_for_box;
212  std::string ref_prefix = pp_prefix + "." + ref_tag_indicator_names[t];
213  update_box_for_refinement(ref_prefix, lev_for_box, real_box, time);
214  ref_tags[t].GetInfo().SetRealBox(real_box);
215  }
216 
217  // This allows dynamic refinement based on the value of the density
218  if (ref_tags[t].Field() == "density")
219  {
220  MultiFab::Copy(*mf,vars_new[levc][Vars::cons],Rho_comp,0,1,1);
221 
222  // Refinement based on a moisture mixing ratio (qv, qc, qi, qr, qs, qg).
223  // The map from name to RhoQ component depends on the moisture model and
224  // is held in solverChoice.moisture_indices.
225  } else if ( (ref_tags[t].Field() == "qv") ||
226  (ref_tags[t].Field() == "qc") ||
227  (ref_tags[t].Field() == "qi") ||
228  (ref_tags[t].Field() == "qr") ||
229  (ref_tags[t].Field() == "qs") ||
230  (ref_tags[t].Field() == "qg") )
231  {
232  const auto& mi = solverChoice.moisture_indices;
233  int qcomp = -1;
234  if (ref_tags[t].Field() == "qv") { qcomp = mi.qv; }
235  else if (ref_tags[t].Field() == "qc") { qcomp = mi.qc; }
236  else if (ref_tags[t].Field() == "qi") { qcomp = mi.qi; }
237  else if (ref_tags[t].Field() == "qr") { qcomp = mi.qr; }
238  else if (ref_tags[t].Field() == "qs") { qcomp = mi.qs; }
239  else if (ref_tags[t].Field() == "qg") { qcomp = mi.qg; }
240  AMREX_ALWAYS_ASSERT(qcomp >= 0);
241  MultiFab::Copy( *mf, vars_new[levc][Vars::cons], qcomp, 0, 1, 1);
242  MultiFab::Divide(*mf, vars_new[levc][Vars::cons], Rho_comp, 0, 1, 1);
243 
244  // qt = total condensed water mixing ratio (qc + qi + qr + qs + qg).
245  // Excludes qv by convention here.
246  } else if (ref_tags[t].Field() == "qt") {
247  const auto& mi = solverChoice.moisture_indices;
248  const int idx_qc = mi.qc, idx_qi = mi.qi, idx_qr = mi.qr,
249  idx_qs = mi.qs, idx_qg = mi.qg;
250  AMREX_ALWAYS_ASSERT(idx_qc >= 0 || idx_qi >= 0 || idx_qr >= 0 ||
251  idx_qs >= 0 || idx_qg >= 0);
252  mf->setVal(0.0);
253  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
254  {
255  const Box& bx = mfi.growntilebox();
256  auto qt_arr = mf->array(mfi);
257  auto cons_arr = vars_new[levc][Vars::cons].const_array(mfi);
258  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
259  const Real rho_inv = Real(1.0) / cons_arr(i,j,k,Rho_comp);
260  Real q = Real(0.0);
261  if (idx_qc >= 0) { q += cons_arr(i,j,k,idx_qc) * rho_inv; }
262  if (idx_qi >= 0) { q += cons_arr(i,j,k,idx_qi) * rho_inv; }
263  if (idx_qr >= 0) { q += cons_arr(i,j,k,idx_qr) * rho_inv; }
264  if (idx_qs >= 0) { q += cons_arr(i,j,k,idx_qs) * rho_inv; }
265  if (idx_qg >= 0) { q += cons_arr(i,j,k,idx_qg) * rho_inv; }
266  qt_arr(i,j,k) = q;
267  });
268  }
269 
270  // This allows dynamic refinement based on the value of the z-component of vorticity
271  } else if (ref_tags[t].Field() == "vorticity" ) {
272  MultiFab mf_cc_vel(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(1,1,1));
273  average_face_to_cellcenter(mf_cc_vel,0,Array<const MultiFab*,3>{&U_new, &V_new, &W_new}, 1);
274 
275  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
276  {
277  const Box& bx = mfi.tilebox();
278  auto& dfab = (*mf)[mfi];
279  auto& sfab = mf_cc_vel[mfi];
280  auto& zfab = (*z_phys_cc[levc])[mfi];
281  derived::erf_dervortz(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
282  }
283 
284  // This allows dynamic refinement based on the value of the scalar/theta
285  } else if ( (ref_tags[t].Field() == "scalar" ) ||
286  (ref_tags[t].Field() == "theta" ) )
287  {
288  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
289  {
290  const Box& bx = mfi.tilebox();
291  auto& dfab = (*mf)[mfi];
292  auto& sfab = vars_new[levc][Vars::cons][mfi];
293  auto& zfab = (*z_phys_cc[levc])[mfi];
294  if (ref_tags[t].Field() == "scalar") {
295  derived::erf_derscalar(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
296  } else if (ref_tags[t].Field() == "theta") {
297  derived::erf_dertheta(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
298  }
299  } // mfi
300  // This allows dynamic refinement based on the value of updraft helicity
301  } else if (ref_tags[t].Field() == "helicity")
302  {
303  MultiFab mf_cc_vel(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(1,1,1));
304  average_face_to_cellcenter(mf_cc_vel,0,Array<const MultiFab*,3>{&U_new, &V_new, &W_new}, 1);
305 
306  // NOTE: helicity is a vertical integral so we must not tile in z
307  for (MFIter mfi(*mf, TileNoZ()); mfi.isValid(); ++mfi)
308  {
309  const Box& bx = mfi.tilebox();
310  auto& dfab = (*mf)[mfi];
311  auto& sfab = mf_cc_vel[mfi];
312  auto& zfab = (*z_phys_cc[levc])[mfi];
313 
314  derived::erf_derhelicity(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
315  }
316  } else if (ref_tags[t].Field() == "max_reflectivity")
317  {
318  // NOTE: this tests the data layout the reflectivity kernel assumes rather
319  // than enumerating moisture models, so it picks up any scheme that
320  // carries rain/snow/graupel in RhoQ4/RhoQ5/RhoQ6
322  // NOTE: this takes a max over the column so we must not tile in z
323  for (MFIter mfi(*mf, TileNoZ()); mfi.isValid(); ++mfi)
324  {
325  const Box& bx = mfi.tilebox();
326  auto& dfab = (*mf)[mfi];
327  auto& sfab = vars_new[levc][Vars::cons][mfi];
328  auto& zfab = (*z_phys_cc[levc])[mfi];
329 
330  derived::erf_dermaxreflectivity(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
331  }
332  } else {
333  Abort("Max reflectivity requires a microphysics model carrying rain, snow and graupel.");
334  }
335  // This allows dynamic refinement based on the terrain blanking
336  } else if ( (SolverChoice::terrain_type == TerrainType::ImmersedForcing) &&
337  (ref_tags[t].Field() == "terrain_blanking") )
338  {
339  MultiFab::Copy(*mf,*terrain_blanking[levc],0,0,1,1);
340  }
341  else if (ref_tags[t].Field() == "velmag")
342  {
343  ParmParse pp(pp_prefix);
344  Vector<std::string> refinement_indicators;
345  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
346  Real velmag_threshold;
347  bool is_hurricane_tracker = false;
348  for (int i=0; i<refinement_indicators.size(); ++i)
349  {
350  if (refinement_indicators[i]=="hurricane_tracker") {
351  is_hurricane_tracker = true;
352  std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
353  ParmParse ppr(ref_prefix);
354  ppr.get("value_greater", velmag_threshold);
355  break;
356  }
357  }
358 
359  Vector<MultiFab> mf_cc_vel(1);
360  mf_cc_vel[0].define(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(0,0,0));
361  average_face_to_cellcenter(mf_cc_vel[0],0,Array<const MultiFab*,3>{&U_new, &V_new, &W_new});
362 
363  if (is_hurricane_tracker) {
364  HurricaneTracker(levc, time, mf_cc_vel[0], velmag_threshold, &tags);
365  } else {
366  for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
367  {
368  const Box& bx = mfi.tilebox();
369  auto& dfab = (*mf)[mfi];
370  auto& sfab = mf_cc_vel[0][mfi];
371  auto& zfab = (*z_phys_cc[levc])[mfi];
372  derived::erf_dermagvel(bx, dfab, 0, 1, sfab, zfab, Geom(levc), time, nullptr, levc);
373  }
374  }
375 
376 #ifdef ERF_USE_PARTICLES
377  } else {
378  //
379  // Particle-derived refinement. Two forms of `field_name` are
380  // supported:
381  // <species>_count : particle count per cell
382  // <species>_<mesh_var> : Eulerian mesh variable provided
383  // by the species' `meshPlotVarNames()`
384  // (e.g. `super_droplets_moisture_mass_density`,
385  // `tracer_particles_mass_density`).
386  //
387  // In both cases the field is deposited at every level in
388  // [levc, finest_level] and averaged down level-by-level so that
389  // a signal present only on a finer level still triggers
390  // refinement at the coarser level being tagged. Without this,
391  // particles localised at level 1 would not register at level 0
392  // and the fine grid would disappear at the next regrid.
393  //
394  const auto& particles_namelist( particleData.getNames() );
395  mf->setVal(0.0);
396  for (ParticlesNamesVector::size_type i = 0; i < particles_namelist.size(); i++)
397  {
398  auto* pc = particleData[particles_namelist[i]];
399  const std::string& sp_name = particles_namelist[i];
400  const std::string& field = ref_tags[t].Field();
401 
402  const std::string count_str = sp_name + "_count";
403  const std::string prefix = sp_name + "_";
404  std::string mesh_var;
405  if (field != count_str
406  && field.size() > prefix.size()
407  && field.compare(0, prefix.size(), prefix) == 0)
408  {
409  const std::string suffix = field.substr(prefix.size());
410  for (const auto& v : pc->meshPlotVarNames()) {
411  if (v == suffix) { mesh_var = v; break; }
412  }
413  }
414  if (field != count_str && mesh_var.empty()) { continue; }
415 
416  pc->resizeData();
417  const int pc_nlevs = static_cast<int>(pc->GetParticles().size());
418 
419  // Deposit at each level into per-level MultiFabs.
420  Vector<MultiFab> per_lev(finest_level+1);
421  for (int lev = levc; lev <= finest_level; lev++) {
422  per_lev[lev].define(grids[lev], dmap[lev], 1, 0);
423  per_lev[lev].setVal(0);
424  if (field == count_str) {
425  if (lev < pc_nlevs) {
426  pc->IncrementWithTotal(per_lev[lev], lev);
427  }
428  } else {
429  pc->computeMeshVar(mesh_var, per_lev[lev],
430  *z_phys_nd[lev], lev);
431  }
432  }
433 
434  // Average down level-by-level from finest to levc. This
435  // avoids multi-level coarsening (e.g. L2->L0 with ratio
436  // (4,1,4)) which can fail when fine-level boxes are not
437  // aligned to the composite refinement ratio.
438  for (int lev = finest_level; lev > levc; lev--) {
439  MultiFab temp_crse(grids[lev-1], dmap[lev-1], 1, 0);
440  temp_crse.setVal(0);
441  average_down(per_lev[lev], temp_crse,
442  0, 1, ref_ratio[lev-1]);
443  MultiFab::Add(per_lev[lev-1], temp_crse, 0, 0, 1, 0);
444  }
445 
446  MultiFab::Copy(*mf, per_lev[levc], 0, 0, 1, 0);
447  }
448 #endif
449  }
450 
451  ref_tags[t](tags,mf.get(),clearval,tagval,time,levc,geom[levc]);
452  } // loop over t
453 
454  // ********************************************************************************************
455  // Refinement based on 2d distance from the "eye" which is defined here as the (x,y) location of
456  // the integrated qv
457  // ********************************************************************************************
458  ParmParse pp(pp_prefix);
459  Vector<std::string> refinement_indicators;
460  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
461  for (int i=0; i<refinement_indicators.size(); ++i)
462  {
463  if ( (refinement_indicators[i]=="storm_tracker") && (solverChoice.moisture_type != MoistureType::None) )
464  {
465  std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
466  ParmParse ppr(ref_prefix);
467 
468  double ref_start_time = -1.0;
469  ppr.queryAdd("start_time",ref_start_time);
470 
471  if (time >= ref_start_time) {
472 
473  Real max_radius = -one;
474  ppr.get("max_radius", max_radius);
475 
476  // Create the volume-weighted sum of (rho qv) in each column
477  MultiFab mf_qv_int(ba2d[levc], dmap[levc], 1, 0); mf_qv_int.setVal(0.);
478 
479  // Define the 2D MultiFab holding the column-integrated (rho qv)
480  volWgtColumnSum(levc, S_new, RhoQ1_comp, mf_qv_int, *detJ_cc[levc]);
481 
482  // Find the max value in the domain
483  IntVect eye = mf_qv_int.maxIndex(0);
484 
485  const auto dx = geom[levc].CellSizeArray();
486  const auto prob_lo = geom[levc].ProbLoArray();
487 
488  Real eye_x = prob_lo[0] + (eye[0] + myhalf) * dx[0];
489  Real eye_y = prob_lo[1] + (eye[1] + myhalf) * dx[1];
490 
491  tag_on_distance_from_eye(geom[levc], &tags, eye_x, eye_y, max_radius);
492  }
493  }
494  }
495 }
void tag_on_distance_from_eye(const Geometry &cgeom, TagBoxArray *tags, const Real eye_x, const Real eye_y, const Real rad_tag)
Tag cells based on 2D distance from the storm eye.
Definition: ERF_RefineHurricane.cpp:111
amrex::Vector< amrex::Vector< amrex::Box > > boxes_at_level
Definition: ERF.H:1195
void HurricaneTracker(int lev, double time, const amrex::MultiFab &cc_vel, const amrex::Real velmag_threshold, amrex::TagBoxArray *tags=nullptr)
Definition: ERF_RefineHurricane.cpp:147
void volWgtColumnSum(int lev, const amrex::MultiFab &mf, int comp, amrex::MultiFab &mf_2d, const amrex::MultiFab &dJ)
Definition: ERF_VolWgtSum.cpp:92
void update_box_for_refinement(std::string &ref_prefix, int &lev_for_box, amrex::RealBox &real_box, const double time)
Definition: ERF_RefineBox.cpp:372
static amrex::Vector< amrex::Vector< std::string > > nc_init_file
Definition: ERF.H:1704
amrex::Vector< amrex::Vector< amrex::BoxArray > > subdomains
Definition: ERF.H:1862
static amrex::Vector< amrex::Vector< int > > have_read_nc_init_file
Definition: ERF.H:1705
static amrex::Vector< amrex::AMRErrorTag > ref_tags
Definition: ERF.H:1852
amrex::Vector< int > num_boxes_at_level
Definition: ERF.H:1193
static amrex::Vector< std::string > ref_tag_indicator_names
Definition: ERF.H:1860
@ q
Definition: ERF_WSM6.H:273
@ t
Definition: ERF_WSM6.H:272
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:702
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:495
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:430
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:335
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:313
void erf_dermaxreflectivity(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:581
integer, private isub
Definition: ERF_module_mp_morr_two_moment.F90:164
bool has_reflectivity_species() const
Test whether the active scheme carries rain, snow and graupel in the RhoQ4/RhoQ5/RhoQ6 slots assumed ...
Definition: ERF_DataStruct.H:531
Here is the call graph for this function:

◆ estTimeStep()

double 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
67 {
68  BL_PROFILE("ERF::estTimeStep()");
69 
70  // Terrain aware (T) and terrain unaware (N) time step estimates.
71  double estdt_comp_T = bogus_large_value;
72  double estdt_comp_N = bogus_large_value;
73  double estdt_lowM_T = bogus_large_value;
74  double estdt_lowM_N = bogus_large_value;
75 
76  // We intentionally use the level 0 domain to compute whether to use this direction in the dt calculation
77  const int nxc = geom[0].Domain().length(0);
78  const int nyc = geom[0].Domain().length(1);
79 
80  auto const dxinv = geom[level].InvCellSizeArray();
81  auto dxinv_EB = dxinv; dxinv_EB[2] = one / dz_min[level];
82 
83  MultiFab const& S_new = vars_new[level][Vars::cons];
84 
85  // Keep the thermodynamic samples alongside the cell-centered velocity so
86  // the wall-rate reduction can call the same pointwise MOST evaluator as
87  // production wall transfer without allocating a second global temporary.
88  MultiFab ccvel_N(grids[level],dmap[level],7,0);
89  MultiFab ccvel_T(grids[level],dmap[level],3,0);
90 
91  int klo = geom[level].Domain().smallEnd(2);
92  int khi = geom[level].Domain().bigEnd(2);
93  MultiFab omega(convert(grids[level],IntVect(0,0,1)),dmap[level],1,0);
94  for (MFIter mfi(vars_new[level][Vars::zvel]); mfi.isValid(); ++mfi)
95  {
96  Box vbx = mfi.validbox();
97 
98  const Array4< Real>& omega_arr = omega.array(mfi);
99  const Array4<const Real>& u_arr = vars_new[level][IntVars::xmom].const_array(mfi);
100  const Array4<const Real>& v_arr = vars_new[level][IntVars::ymom].const_array(mfi);
101  const Array4<const Real>& w_arr = vars_new[level][IntVars::zmom].const_array(mfi);
102 
103  const Array4<const Real>& z_nd_arr = z_phys_nd[level]->const_array(mfi);
104 
105  const Array4<const Real>& mf_ux = mapfac[level][MapFacType::u_x]->const_array(mfi);
106  const Array4<const Real>& mf_vy = mapfac[level][MapFacType::v_y]->const_array(mfi);
107 
108  ParallelFor(vbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
109  {
110  if (k==klo || k==(khi+1)) {
111  omega_arr(i,j,k) = zero;
112  } else {
113  omega_arr(i,j,k) = OmegaFromW(i,j,k,w_arr(i,j,k),
114  u_arr,v_arr,mf_ux,mf_vy,
115  z_nd_arr,dxinv);
116  }
117  });
118  }
119 
120  average_face_to_cellcenter(ccvel_T,0,
121  Array<const MultiFab*,3>{&vars_new[level][Vars::xvel],
122  &vars_new[level][Vars::yvel],
123  &omega});
124  average_face_to_cellcenter(ccvel_N,0,
125  Array<const MultiFab*,3>{&vars_new[level][Vars::xvel],
126  &vars_new[level][Vars::yvel],
127  &vars_new[level][Vars::zvel],});
128 
129  const bool chamber_cloudy = cloud_chamber_config.active &&
131  const Real rdOcp = solverChoice.rdOcp;
132  const MultiFab& chamber_base_state = base_state[level];
133  for (MFIter mfi(S_new); mfi.isValid(); ++mfi) {
134  const Array4<const Real> state = S_new.const_array(mfi);
135  const Array4<const Real> base = chamber_base_state.const_array(mfi);
136  const Array4<Real> velocity = ccvel_N.array(mfi);
137  const Box bx = mfi.validbox();
138  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
139  {
140  const Real rho = state(i,j,k,Rho_comp);
141  velocity(i,j,k,3) = state(i,j,k,RhoTheta_comp) / rho;
142  velocity(i,j,k,4) = chamber_cloudy ?
143  state(i,j,k,RhoQ1_comp) / rho : Real(0.0);
144  velocity(i,j,k,5) = base(i,j,k,BaseState::p0_comp);
145  velocity(i,j,k,6) = rdOcp;
146  });
147  }
148 
149  bool l_substepping = (solverChoice.substepping_type[level] == SubsteppingType::Implicit);
150  int l_anelastic = solverChoice.anelastic[level];
151 
152  bool l_comp_substepping_diag = (verbose && l_substepping && !l_anelastic && solverChoice.substepping_diag);
153 
154  Real estdt_comp_inv_N, estdt_comp_inv_T;
155  Real estdt_lowM_inv_N, estdt_lowM_inv_T;
156  Real estdt_vert_comp_inv, estdt_vert_lowM_inv;
157 
158  const MultiFab& z_nd_mf = *z_phys_nd[level];
159 
160  if (l_substepping && (nxc==1) && (nyc==1)) {
161  // SCM -- should not depend on dx or dy; force minimum number of substeps
162  estdt_comp_inv_T = std::numeric_limits<Real>::min();
163  estdt_comp_inv_N = estdt_comp_inv_T;
164  }
165  else if (solverChoice.terrain_type == TerrainType::EB)
166  {
167  const eb_& eb_lev = get_eb(level);
168  const MultiFab& detJ = (eb_lev.get_const_factory())->getVolFrac();
169 
170  estdt_comp_inv_N = ReduceMax(S_new, ccvel_N, detJ, 0,
171  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
172  Array4<Real const> const& s,
173  Array4<Real const> const& u,
174  Array4<Real const> const& vf) -> Real
175  {
176  Real new_comp_dt = -bogus_large_value;
177  amrex::Loop(b, [=,&new_comp_dt] (int i, int j, int k) noexcept
178  {
179  if (vf(i,j,k) > zero)
180  {
181  const Real rho = s(i, j, k, Rho_comp);
182  const Real rhotheta = s(i, j, k, RhoTheta_comp);
183 
184  // NOTE: even when moisture is present,
185  // we only use the partial pressure of the dry air
186  // to compute the soundspeed
187  Real pressure = getPgivenRTh(rhotheta);
188  Real c = std::sqrt(Gamma * pressure / rho);
189 
190  // If we are doing implicit acoustic substepping, then the z-direction does not contribute
191  // to the computation of the time step
192  if (l_substepping) {
193  if ((nxc > 1) && (nyc==1)) {
194  // 2-D in x-z
195  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv_EB[0]), new_comp_dt);
196  } else if ((nyc > 1) && (nxc==1)) {
197  // 2-D in y-z
198  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,1))+c)*dxinv_EB[1]), new_comp_dt);
199  } else {
200  // 3-D
201  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv_EB[0]),
202  ((amrex::Math::abs(u(i,j,k,1))+c)*dxinv_EB[1]), new_comp_dt);
203  }
204 
205  // If we are not doing implicit acoustic substepping, then the z-direction contributes
206  // to the computation of the time step
207  } else {
208  if (nxc > 1 && nyc > 1) {
209  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv_EB[0]),
210  ((amrex::Math::abs(u(i,j,k,1))+c)*dxinv_EB[1]),
211  ((amrex::Math::abs(u(i,j,k,2))+c)*dxinv_EB[2]), new_comp_dt);
212  } else if (nxc > 1) {
213  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,0))+c)*dxinv_EB[0]),
214  ((amrex::Math::abs(u(i,j,k,2))+c)*dxinv_EB[2]), new_comp_dt);
215  } else if (nyc > 1) {
216  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,1))+c)*dxinv_EB[1]),
217  ((amrex::Math::abs(u(i,j,k,2))+c)*dxinv_EB[2]), new_comp_dt);
218  } else {
219  new_comp_dt = amrex::max(((amrex::Math::abs(u(i,j,k,2))+c)*dxinv_EB[2]), new_comp_dt);
220  }
221 
222  }
223  }
224  });
225  return new_comp_dt;
226  });
227 
228  // The metric terms do not exist for EB, so the terrain aware
229  // estimate is identical to the terrain unaware estimate
230  estdt_comp_inv_T = estdt_comp_inv_N;
231 
232  } else {
233  // One pass over the data returning both the terrain aware (T) estimate,
234  // and the terrain unaware (N) estimate
235  ReduceOps<ReduceOpMax,ReduceOpMax> reduce_op;
236  ReduceData<Real,Real> reduce_data(reduce_op);
237 
238 #ifdef AMREX_USE_OMP
239 #pragma omp parallel if (Gpu::notInLaunchRegion())
240 #endif
241  for (MFIter mfi(S_new,TilingIfNotGPU()); mfi.isValid(); ++mfi)
242  {
243  const Box& bx = mfi.tilebox();
244 
245  const Array4<const Real>& s = S_new.const_array(mfi);
246  const Array4<const Real>& u_T = ccvel_T.const_array(mfi);
247  const Array4<const Real>& u_N = ccvel_N.const_array(mfi);
248  const Array4<const Real>& z_nd = z_nd_mf.const_array(mfi);
249 
250  reduce_op.eval(bx, reduce_data,
251  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> GpuTuple<Real,Real>
252  {
253  const Real rho = s(i,j,k,Rho_comp);
254  const Real rhotheta = s(i,j,k,RhoTheta_comp);
255 
256  // NOTE: even when moisture is present,
257  // we only use the partial pressure of the dry air
258  // to compute the soundspeed
259  Real pressure = getPgivenRTh(rhotheta);
260  Real c = std::sqrt(Gamma * pressure / rho);
261 
262  Real inv_dt_T = Compute_InvDt_Compressible(i,j,k, c,
263  u_T(i,j,k,0), u_T(i,j,k,1), u_T(i,j,k,2),
264  z_nd, dxinv, true, l_substepping, nxc, nyc);
265 
266  Real inv_dt_N = Compute_InvDt_Compressible(i,j,k, c,
267  u_N(i,j,k,0), u_N(i,j,k,1), u_N(i,j,k,2),
268  z_nd, dxinv, false, l_substepping, nxc, nyc);
269 
270  return {inv_dt_T, inv_dt_N};
271  });
272  }
273 
274  GpuTuple<Real,Real> hv = reduce_data.value(reduce_op);
275  estdt_comp_inv_T = amrex::get<0>(hv);
276  estdt_comp_inv_N = amrex::get<1>(hv);
277  } // not EB
278 
279  {
280  Real comp_inv[2] = {estdt_comp_inv_T, estdt_comp_inv_N};
281  ParallelDescriptor::ReduceRealMax(comp_inv,2);
282  estdt_comp_inv_T = comp_inv[0];
283  estdt_comp_inv_N = comp_inv[1];
284  }
285 
286  // Globally empty level -> ReduceMax = lowest(); treat level as non-constraining.
287  estdt_comp_T = (estdt_comp_inv_T > zero) ? (cfl / estdt_comp_inv_T) : bogus_large_value;
288  estdt_comp_N = (estdt_comp_inv_N > zero) ? (cfl / estdt_comp_inv_N) : bogus_large_value;
289 
290  //
291  // Anelastic (low Mach) estimate -- purely advective, again terrain aware
292  // and terrain unaware
293  //
294  if (solverChoice.terrain_type == TerrainType::EB)
295  {
296  estdt_lowM_inv_N = ReduceMax(ccvel_N, z_nd_mf, 0,
297  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
298  Array4<Real const> const& u,
299  Array4<Real const> const& z_nd) -> Real
300  {
301  Real new_lm_dt = -bogus_large_value;
302  Loop(b, [=,&new_lm_dt] (int i, int j, int k) noexcept
303  {
304  Real inv_dt_lowM_N = Compute_InvDt_Anelastic(i,j,k,
305  u(i,j,k,0), u(i,j,k,1), u(i,j,k,2),
306  z_nd, dxinv_EB, false);
307  new_lm_dt = amrex::max(inv_dt_lowM_N, new_lm_dt);
308  });
309  return new_lm_dt;
310  });
311 
312  // The metric terms do not exist for EB
313  estdt_lowM_inv_T = estdt_lowM_inv_N;
314 
315  } else {
316 
317  ReduceOps<ReduceOpMax,ReduceOpMax> reduce_op;
318  ReduceData<Real,Real> reduce_data(reduce_op);
319 
320 #ifdef AMREX_USE_OMP
321 #pragma omp parallel if (Gpu::notInLaunchRegion())
322 #endif
323  for (MFIter mfi(ccvel_T,TilingIfNotGPU()); mfi.isValid(); ++mfi)
324  {
325  const Box& bx = mfi.tilebox();
326 
327  const Array4<const Real>& u_T = ccvel_T.const_array(mfi);
328  const Array4<const Real>& u_N = ccvel_N.const_array(mfi);
329  const Array4<const Real>& z_nd = z_nd_mf.const_array(mfi);
330 
331  reduce_op.eval(bx, reduce_data,
332  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> GpuTuple<Real,Real>
333  {
334  Real inv_dt_T = Compute_InvDt_Anelastic(i,j,k,
335  u_T(i,j,k,0), u_T(i,j,k,1), u_T(i,j,k,2),
336  z_nd, dxinv, true);
337 
338  Real inv_dt_N = Compute_InvDt_Anelastic(i,j,k,
339  u_N(i,j,k,0), u_N(i,j,k,1), u_N(i,j,k,2),
340  z_nd, dxinv, false);
341 
342  return {inv_dt_T, inv_dt_N};
343  });
344  }
345 
346  GpuTuple<Real,Real> hv = reduce_data.value(reduce_op);
347  estdt_lowM_inv_T = amrex::get<0>(hv);
348  estdt_lowM_inv_N = amrex::get<1>(hv);
349  }
350 
351  {
352  Real lowM_inv[2] = {estdt_lowM_inv_T, estdt_lowM_inv_N};
353  ParallelDescriptor::ReduceRealMax(lowM_inv,2);
354  estdt_lowM_inv_T = lowM_inv[0];
355  estdt_lowM_inv_N = lowM_inv[1];
356  }
357 
358  if (estdt_lowM_inv_T > zero) { estdt_lowM_T = cfl / estdt_lowM_inv_T; }
359  if (estdt_lowM_inv_N > zero) { estdt_lowM_N = cfl / estdt_lowM_inv_N; }
360 
361  Real max_wall_rate = Real(0.0);
362  Real estdt_wall = bogus_large_value;
366  const auto walls = cloud_chamber_config.wall_boundary();
367  const Box domain = geom[level].Domain();
368  max_wall_rate = ReduceMax(ccvel_N, 0,
369  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
370  Array4<Real const> const& velocity) -> Real
371  {
372  Real rate = Real(0.0);
373  amrex::Loop(b, [=,&rate] (int i, int j, int k) noexcept
374  {
375  // A free staggered component can receive tangential traction
376  // from every active perpendicular wall at an edge/corner.
377  // Fixed and neutral coefficients use the exact local
378  // velocity-parallel Jacobian row-sum factor. MOST evaluates
379  // its current coefficients once per wall/cell and uses that
380  // state as a frozen-coefficient local rate estimate; it is
381  // not a nonlinear MOST Jacobian bound.
382  Real momentum_rate = Real(0.0);
383  amrex::GpuArray<Real, AMREX_SPACEDIM> low_momentum_rates{};
384  amrex::GpuArray<Real, AMREX_SPACEDIM> high_momentum_rates{};
385  // Evaluate each encountered wall/cell state once. The
386  // component row sums below only compose these retained
387  // per-face rates; they must not repeat the MOST solve.
388  for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
389  const bool low = (dir == 0 ? i == domain.smallEnd(0) :
390  (dir == 1 ? j == domain.smallEnd(1) :
391  k == domain.smallEnd(2)));
392  const bool high = (dir == 0 ? i == domain.bigEnd(0) :
393  (dir == 1 ? j == domain.bigEnd(1) :
394  k == domain.bigEnd(2)));
395  if (low) {
396  const auto& wall = walls[2*dir];
399  const Real U_t =
402  dir, i, j, k, velocity, wall);
403  const auto runtime =
405  wall, velocity(i,j,k,3), velocity(i,j,k,4),
406  velocity(i,j,k,5), velocity(i,j,k,6), U_t,
407  Real(0.5) / dxinv[dir],
408  dir == 2 ? 1 : 0);
409  rate = amrex::max(rate,
411  wall, U_t, dxinv[dir], runtime));
412  low_momentum_rates[dir] =
414  wall, U_t, dxinv[dir], runtime);
415  }
416  }
417  if (high) {
418  const auto& wall = walls[2*dir+1];
421  const Real U_t =
424  dir, i, j, k, velocity, wall);
425  const auto runtime =
427  wall, velocity(i,j,k,3), velocity(i,j,k,4),
428  velocity(i,j,k,5), velocity(i,j,k,6), U_t,
429  Real(0.5) / dxinv[dir],
430  dir == 2 ? -1 : 0);
431  rate = amrex::max(rate,
433  wall, U_t, dxinv[dir], runtime));
434  high_momentum_rates[dir] =
436  wall, U_t, dxinv[dir], runtime);
437  }
438  }
439  }
440  for (int component = 0; component < AMREX_SPACEDIM; ++component) {
441  const Real component_rate =
443  component, low_momentum_rates, high_momentum_rates);
444  momentum_rate = amrex::max(momentum_rate, component_rate);
445  }
446  rate = amrex::max(rate, momentum_rate);
447  });
448  return rate;
449  });
450  ParallelDescriptor::ReduceRealMax(max_wall_rate);
451  if (max_wall_rate > Real(0.0)) {
452  estdt_wall = erf_cloud_chamber_wall_flux::wall_dt_from_max_rate(max_wall_rate);
453  }
454  }
455  // Wall-rate kernels and reductions use amrex::Real, while ERF's host
456  // timestep estimates are double even in ERF_PRECISION=SINGLE builds.
457  const double estdt_wall_host = static_cast<double>(estdt_wall);
458  estdt_comp_T = std::min(estdt_comp_T, estdt_wall_host);
459  estdt_comp_N = std::min(estdt_comp_N, estdt_wall_host);
460  estdt_lowM_T = std::min(estdt_lowM_T, estdt_wall_host);
461  estdt_lowM_N = std::min(estdt_lowM_N, estdt_wall_host);
462 
463  const double fixed_dt_level = static_cast<double>(fixed_dt[level]);
465  level, fixed_dt_level, estdt_wall_host, max_wall_rate);
466 
467  // Additional vertical diagnostics
468  if (l_comp_substepping_diag) {
469  estdt_vert_comp_inv = ReduceMax(S_new, ccvel_T, z_nd_mf, 0,
470  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
471  Array4<Real const> const& s,
472  Array4<Real const> const& u,
473  Array4<Real const> const& z_nd) -> Real
474  {
475  Real new_comp_dt = -bogus_large_value;
476  amrex::Loop(b, [=,&new_comp_dt] (int i, int j, int k) noexcept
477  {
478  {
479  const Real rho = s(i, j, k, Rho_comp);
480  const Real rhotheta = s(i, j, k, RhoTheta_comp);
481 
482  // NOTE: even when moisture is present,
483  // we only use the partial pressure of the dry air
484  // to compute the soundspeed
485  Real pressure = getPgivenRTh(rhotheta);
486  Real c = std::sqrt(Gamma * pressure / rho);
487 
488  Real h_zeta = Compute_h_zeta_AtCellCenter(i,j,k,dxinv,z_nd);
489  Real idz_loc = dxinv[2] / h_zeta;
490 
491  // Look at z-direction only
492  new_comp_dt = amrex::max((amrex::Math::abs(u(i,j,k,2)) + c) * idz_loc, new_comp_dt);
493  }
494  });
495  return new_comp_dt;
496  });
497 
498  estdt_vert_lowM_inv = ReduceMax(ccvel_T, z_nd_mf, 0,
499  [=] AMREX_GPU_HOST_DEVICE (Box const& b,
500  Array4<Real const> const& u,
501  Array4<Real const> const& z_nd) -> Real
502  {
503  Real new_lowM_dt = -bogus_large_value;
504  amrex::Loop(b, [=,&new_lowM_dt] (int i, int j, int k) noexcept
505  {
506  Real h_zeta = Compute_h_zeta_AtCellCenter(i,j,k,dxinv,z_nd);
507  Real idz_loc = dxinv[2] / h_zeta;
508  new_lowM_dt = amrex::max((amrex::Math::abs(u(i,j,k,2))) * idz_loc, new_lowM_dt);
509  });
510  return new_lowM_dt;
511  });
512 
513  ParallelDescriptor::ReduceRealMax(estdt_vert_comp_inv);
514  ParallelDescriptor::ReduceRealMax(estdt_vert_lowM_inv);
515  }
516 
517  if (verbose) {
518  // Terrain aware (T): includes h_xi, h_eta and h_zeta
519  // Terrain unaware (N): no metric terms, dxinv[2] used for the vertical spacing
520  if (fixed_dt[level] <= zero) {
521  Print() << "Using cfl = " << cfl << " and dx/dy/dz_min = " <<
522  one/dxinv[0] << " " << one/dxinv[1] << " " << dz_min[level] << std::endl;
523  Print() << "Compressible dt at level " << level << ": "
524  << estdt_comp_T << " (terrain aware) "
525  << estdt_comp_N << " (terrain unaware)" << std::endl;
526  if (estdt_lowM_inv_T > 0.0_rt) {
527  Print() << "Anelastic dt at level " << level << ": "
528  << estdt_lowM_T << " (terrain aware) "
529  << estdt_lowM_N << " (terrain unaware)" << std::endl;
530  } else {
531  Print() << "Anelastic dt at level " << level << ": undefined " << std::endl;
532  }
533  }
534 
535  if (fixed_dt[level] > zero) {
536  Print() << "Based on cfl of one " << std::endl;
537  Print() << "Compressible dt at level " << level << " would be: "
538  << estdt_comp_T/cfl << " (terrain aware) "
539  << estdt_comp_N/cfl << " (terrain unaware)" << std::endl;
540  if (estdt_lowM_inv_T > zero) {
541  Print() << "Anelastic dt at level " << level << " would be: "
542  << estdt_lowM_T/cfl << " (terrain aware) "
543  << estdt_lowM_N/cfl << " (terrain unaware)" << std::endl;
544  } else {
545  Print() << "Anelastic dt at level " << level << " would be undefined " << std::endl;
546  }
547  Print() << "Fixed dt at level " << level << " is: " << fixed_dt[level] << std::endl;
548  if (fixed_fast_dt[level] > zero) {
549  Print() << "Fixed fast dt at level " << level << " is: " << fixed_fast_dt[level] << std::endl;
550  }
551  }
552  }
553 
554  if (solverChoice.substepping_type[level] != SubsteppingType::None) {
555  if (fixed_dt[level] > zero && fixed_fast_dt[level] > zero) {
556  dt_fast_ratio = static_cast<long>( fixed_dt[level] / fixed_fast_dt[level] );
557  if (dt_fast_ratio < 1) {
558  Abort("Invalid fixed_fast_dt: must be <= fixed_dt so mri_dt_ratio >= 1");
559  }
560  } else if (fixed_dt[level] > zero) {
561  // Max CFL_c = one for substeps by default, but we enforce a min of 4 substeps
562  auto dt_sub_max = (estdt_comp_T * (third/cfl) * sub_cfl);
563  dt_fast_ratio = static_cast<long>( std::max(fixed_dt[level]/static_cast<double>(dt_sub_max), 4.0) );
564  } else {
565  // auto dt_sub_max = (estdt_comp_T/cfl * sub_cfl);
566  // dt_fast_ratio = static_cast<long>( std::max(estdt_comp_T/dt_sub_max,Real(4.)) );
567  dt_fast_ratio = static_cast<long>( std::max((cfl/third) / sub_cfl, Real(4.)) );
568  }
569 
570  // Force time step ratio to be an even value
572  if ( dt_fast_ratio%2 != 0) dt_fast_ratio += 1;
573  } else {
574  if ( dt_fast_ratio%6 != 0) {
575  Print() << "mri_dt_ratio = " << dt_fast_ratio
576  << " not divisible by 6 for N/3 substeps in stage 1" << std::endl;
577  dt_fast_ratio = static_cast<int>(std::ceil(dt_fast_ratio/Real(6.0)) * 6);
578  }
579  }
580 
581  if (verbose) {
582  Print() << "smallest even ratio is: " << dt_fast_ratio << std::endl;
583  }
584  } // if substepping
585 
586  // Print out some extra diagnostics -- dt calcs are repeated so as to not
587  // disrupt the overall code flow...
588  if (l_comp_substepping_diag) {
589  double dt_diag = (fixed_dt[level] > zero) ? fixed_dt[level] : static_cast<double>(estdt_comp_T);
590  int ns = (fixed_mri_dt_ratio > zero) ? fixed_mri_dt_ratio : dt_fast_ratio;
591 
592  // horizontal acoustic CFL must be < 1 (fully explicit)
593  // vertical acoustic CFL may be > 1
594  Print() << "effective horiz,vert acoustic CFL with " << ns << " substeps : "
595  << (dt_diag / ns) * estdt_comp_inv_T << " "
596  << (dt_diag / ns) * estdt_vert_comp_inv << std::endl;
597 
598  // vertical advective CFL should be < 1, otherwise w-damping may be needed
599  Print() << "effective vert advective CFL : "
600  << dt_diag * estdt_vert_lowM_inv << std::endl;
601  }
602 
603  if (fixed_dt[level] > zero) {
604  return fixed_dt[level];
605  } else {
606  // Anelastic (substepping is not allowed)
607  if (l_anelastic) {
608 
609  // Make sure that timestep is less than the dt_max
610  estdt_lowM_T = std::min(estdt_lowM_T, dt_max);
611 
612  // On the first timestep enforce dt_max_initial
613  if (istep[level] == 0) {
614  return std::min(dt_max_initial, estdt_lowM_T);
615  } else {
616  return estdt_lowM_T;
617  }
618 
619 
620  // Compressible with or without substepping
621  } else {
622  return estdt_comp_T;
623  }
624  }
625 }
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:54
constexpr amrex::Real third
Definition: ERF_NumericalConstants.H:36
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:791
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE 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:190
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_InvDt_Compressible(const int i, const int j, const int k, const amrex::Real c, const amrex::Real u, const amrex::Real v, const amrex::Real w, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxinv, const bool l_terrain_aware, const bool l_substepping, const int nxc=2, const int nyc=2)
Definition: ERF_TimestepUtils.H:48
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_InvDt_Anelastic(const int i, const int j, const int k, const amrex::Real u, const amrex::Real v, const amrex::Real w, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxinv, const bool l_terrain_aware)
Definition: ERF_TimestepUtils.H:155
amrex::Vector< double > fixed_fast_dt
Definition: ERF.H:1517
static amrex::Real cfl
Definition: ERF.H:1508
static double dt_max
Definition: ERF.H:1513
amrex::Vector< double > fixed_dt
Definition: ERF.H:1516
static amrex::Real sub_cfl
Definition: ERF.H:1509
Owns and exposes cell-centered and face-centered EB factories.
Definition: ERF_EB.H:24
@ ns
Definition: ERF_Morrison.H:49
@ omega
Definition: ERF_Morrison.H:55
void enforce_fixed_dt_limit(int level, double fixed_dt, double wall_dt, amrex::Real max_wall_rate)
Definition: ERF_CloudChamberWallDtGuard.H:19
Definition: ERF_CloudChamberWallFlux.H:21
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real tangential_speed_cell_centered(int dir, int i, int j, int k, const amrex::Array4< const amrex::Real > &velocity, const erf_wall_thermodynamics::FaceWall &wall) noexcept
Definition: ERF_CloudChamberWallFlux.H:674
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real wall_rate_for_face(const erf_wall_thermodynamics::FaceWall &wall, amrex::Real U_t, amrex::Real dx_inv, const FaceRuntimeCoefficients &runtime) noexcept
Definition: ERF_CloudChamberWallFlux.H:486
AMREX_FORCE_INLINE amrex::Real wall_dt_from_max_rate(amrex::Real max_rate, amrex::Real wall_cfl=amrex::Real(0.5)) noexcept
Definition: ERF_CloudChamberWallFlux.H:1077
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool wall_rate_requires_tangential_speed(const erf_wall_thermodynamics::FaceWall &wall) noexcept
Definition: ERF_CloudChamberWallFlux.H:475
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE FaceRuntimeCoefficients most_wall_coefficients(const erf_wall_thermodynamics::FaceWall &wall, amrex::Real theta_air, amrex::Real qv_air, amrex::Real p_hse, amrex::Real rdOcp, amrex::Real U_t, amrex::Real wall_distance, int gravity_sign) noexcept
Definition: ERF_CloudChamberWallFlux.H:278
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real momentum_row_sum_rate(int component, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &low_rate, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &high_rate) noexcept
Definition: ERF_CloudChamberWallFlux.H:393
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real momentum_rate_for_face(const erf_wall_thermodynamics::FaceWall &wall, amrex::Real U_t, amrex::Real dx_inv, const FaceRuntimeCoefficients &runtime=FaceRuntimeCoefficients{}) noexcept
Definition: ERF_CloudChamberWallFlux.H:410
amrex::Vector< SubsteppingType > substepping_type
Acoustic substepping type for each AMR level.
Definition: ERF_DataStruct.H:1980
bool substepping_diag
Whether to emit extra CFL diagnostics for compressible substepping.
Definition: ERF_DataStruct.H:1989
int force_stage1_single_substep
Flag forcing a single substep in the first RK stage.
Definition: ERF_DataStruct.H:1978
bool cloudy
Definition: ERF_CloudChamber.H:42
bool has_wall_rate_channel() const noexcept
Definition: ERF_CloudChamber.H:83
Here is the call graph for this function:

◆ Evolve()

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

Referenced by main().

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

◆ EvolveOneStep()

double ERF::EvolveOneStep ( double  time,
double  dt_request 
)
222 {
223  double cur_time = t_new[0];
224  const int step = istep[0];
225 
226  if (start_time + cur_time >= stop_time) {
227  return 0.0;
228  }
229 
230  ComputeDt(step);
231 
232  int iteration = 1;
233  timeStep(0, cur_time, iteration);
234  cur_time += dt[0];
235 
236  post_timestep(step, cur_time, dt[0]);
237 
238  // ****************************************************************************************
239  // Write plotfiles at intermediate times
240  // ****************************************************************************************
241  WriteAtIntermediateTime(step, cur_time);
242 
243  return dt[0];
244 }
Here is the call graph for this function:

◆ fill_from_bndryregs()

void ERF::fill_from_bndryregs ( const amrex::Vector< amrex::MultiFab * > &  mfs,
double  time 
)

Impose boundary conditions using data read in as BndryRegisters from a previous ERF run

Parameters
[in,out]mfsVector of MultiFabs to be filled
[in]timetime at which the data should be filled
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:43
#define NSCALARS
Definition: ERF_IndexDefines.H:16
amrex::Gpu::DeviceVector< amrex::BCRec > domain_bcs_type_d
Definition: ERF.H:1430
@ RhoScalar_bc_comp
Definition: ERF_IndexDefines.H:93
@ ext_dir_ingested
Definition: ERF_IndexDefines.H:301
Here is the call graph for this function:

◆ fill_rhs()

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

◆ fill_terrain_surface()

void ERF::fill_terrain_surface ( int  lev,
amrex::MultiFab &  z_phys,
double  elapsed_time 
)
853 {
854  int ngrow = ComputeGhostCells(solverChoice) + 2;
855  Box bx(surroundingNodes(Geom(lev).Domain())); bx.grow(ngrow);
856  FArrayBox terrain_fab(makeSlab(bx,2,0),1);
857 
858  //
859  // If we are using fitted mesh then we use the surface as defined above
860  // If we are not using fitted mesh but are using z_levels, we still need z_phys (for now)
861  // but we need to use a flat terrain for the mesh itself (the EB data has already been made
862  // from the correct terrain)
863  //
864  if (solverChoice.terrain_type != TerrainType::StaticFittedMesh &&
865  solverChoice.terrain_type != TerrainType::MovingFittedMesh) {
866  terrain_fab.template setVal<RunOn::Device>(zero);
867  } else {
868  //
869  // Fill the values of the terrain height at k=0 only
870  //
871  prob->init_terrain_surface(geom[lev],terrain_fab,elapsed_time);
872  }
873 
874  for (MFIter mfi(z_phys,TilingIfNotGPU()); mfi.isValid(); ++mfi)
875  {
876  Box isect = terrain_fab.box() & z_phys[mfi].box();
877  if (!isect.isEmpty()) {
878  z_phys[mfi].template copy<RunOn::Device>(terrain_fab,isect,0,isect,0,1);
879  }
880  }
881 }
static AMREX_FORCE_INLINE int ComputeGhostCells(const SolverChoice &sc)
Definition: ERF.H:1868

◆ FillBdyCCVels()

void ERF::FillBdyCCVels ( amrex::MultiFab &  mf_cc_vel,
amrex::Geometry &  lev_geom 
)

Fill boundary ghost cells for cell-centered velocities.

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

◆ FillCoarsePatch()

void ERF::FillCoarsePatch ( int  lev,
double  time 
)
private

Fill valid and ghost data. This version fills an entire MultiFab by interpolating from the coarser level – this is used only when a new level of refinement is being created during a run (i.e not at initialization) This will never be used with static refinement.

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

Fill forecast state MultiFabs by interpolating from binary forecast files.

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

Fill valid and ghost data This version fills mfs in valid regions with the values in "mfs" when it is passed in; it is used only to compute ghost values for intermediate stages of a time integrator.

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

Fill valid and ghost data on the coarse level with state data at the given time.

Parameters
[in]levcoarse level to fill
[in]time_dtime at which the data should be filled
[in,out]mfs_velVector of MultiFabs to be filled containing cons, xvel, yvel, and zvel
[in]cons_onlywhether to fill only conserved variables
306 {
307  BL_PROFILE_VAR("ERF::FillPatchCrseLevel()",ERF_FillPatchCrseLevel);
308 
309  AMREX_ALWAYS_ASSERT(lev == 0);
310 
311  Real time = static_cast<Real>(time_d);
312 
313  IntVect ngvect_cons = mfs_vel[Vars::cons]->nGrowVect();
314  IntVect ngvect_vels = mfs_vel[Vars::xvel]->nGrowVect();
315 
316  Vector<Real> ftime = {static_cast<Real>(t_old[lev]), static_cast<Real>(t_new[lev])};
317 
318  //
319  // Below we call FillPatchSingleLevel which does NOT fill ghost cells outside the domain
320  //
321 
322  Vector<MultiFab*> fmf;
323  Vector<MultiFab*> fmf_u;
324  Vector<MultiFab*> fmf_v;
325  Vector<MultiFab*> fmf_w;
326 
327  if (amrex::almostEqual(time,ftime[0])) {
328  fmf = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::cons]};
329  } else if (amrex::almostEqual(time,ftime[1])) {
330  fmf = {&vars_new[lev][Vars::cons], &vars_new[lev][Vars::cons]};
331  } else {
332  fmf = {&vars_old[lev][Vars::cons], &vars_new[lev][Vars::cons]};
333  }
334 
335  const int ncomp = mfs_vel[Vars::cons]->nComp();
336 
337  FillPatchSingleLevel(*mfs_vel[Vars::cons], ngvect_cons, time, fmf, IntVect(0,0,0), ftime,
338  0, 0, ncomp, geom[lev]);
339 
340  if (!cons_only) {
341  if (amrex::almostEqual(time,ftime[0])) {
342  fmf_u = {&vars_old[lev][Vars::xvel], &vars_old[lev][Vars::xvel]};
343  fmf_v = {&vars_old[lev][Vars::yvel], &vars_old[lev][Vars::yvel]};
344  fmf_w = {&vars_old[lev][Vars::zvel], &vars_old[lev][Vars::zvel]};
345  } else if (amrex::almostEqual(time,ftime[1])) {
346  fmf_u = {&vars_new[lev][Vars::xvel], &vars_new[lev][Vars::xvel]};
347  fmf_v = {&vars_new[lev][Vars::yvel], &vars_new[lev][Vars::yvel]};
348  fmf_w = {&vars_new[lev][Vars::zvel], &vars_new[lev][Vars::zvel]};
349  } else {
350  fmf_u = {&vars_old[lev][Vars::xvel], &vars_new[lev][Vars::xvel]};
351  fmf_v = {&vars_old[lev][Vars::yvel], &vars_new[lev][Vars::yvel]};
352  fmf_w = {&vars_old[lev][Vars::zvel], &vars_new[lev][Vars::zvel]};
353  }
354  FillPatchSingleLevel(*mfs_vel[Vars::xvel], ngvect_vels, time, fmf_u,
355  IntVect(0,0,0), ftime, 0, 0, 1, geom[lev]);
356 
357  FillPatchSingleLevel(*mfs_vel[Vars::yvel], ngvect_vels, time, fmf_v,
358  IntVect(0,0,0), ftime, 0, 0, 1, geom[lev]);
359 
360  FillPatchSingleLevel(*mfs_vel[Vars::zvel], ngvect_vels, time, fmf_w,
361  IntVect(0,0,0), ftime, 0, 0, 1, geom[lev]);
362  } // !cons_only
363 
364  // ***************************************************************************
365  // Physical bc's at domain boundary
366  // ***************************************************************************
367  int icomp_cons = 0;
368  int ncomp_cons = mfs_vel[Vars::cons]->nComp();
369 
370  bool do_fb = true;
371 
372 #ifdef ERF_USE_NETCDF
373  if(solverChoice.use_real_bcs && (lev==0)) {
374  fill_from_realbdy(mfs_vel,time,cons_only,icomp_cons,ncomp_cons,ngvect_cons,ngvect_vels);
375  do_fb = false;
376  }
377 #endif
378 
379  if (m_r2d && !solverChoice.use_real_bcs) { fill_from_bndryregs(mfs_vel,time); }
380 
381  // We call this even if use_real_bcs is true because these will fill the vertical bcs
382  // Note that we call FillBoundary inside the physbcs call
383  (*physbcs_cons[lev])(*mfs_vel[Vars::cons],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
384  icomp_cons,ncomp_cons,ngvect_cons,time,BCVars::cons_bc, do_fb);
385  if (!cons_only) {
386  (*physbcs_u[lev])(*mfs_vel[Vars::xvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
387  ngvect_vels,time,BCVars::xvel_bc, do_fb);
388  (*physbcs_v[lev])(*mfs_vel[Vars::yvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
389  ngvect_vels,time,BCVars::yvel_bc, do_fb);
390  (*physbcs_w[lev])(*mfs_vel[Vars::zvel],*mfs_vel[Vars::xvel],*mfs_vel[Vars::yvel],
391  ngvect_vels,time,BCVars::zvel_bc, do_fb);
392  }
393 }
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

Fill valid and ghost data with the "state data" at the given time NOTE: THIS OPERATES ON VELOCITY (MOMENTA ARE JUST TEMPORARIES)

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

◆ FillPlot2DVars()

void ERF::FillPlot2DVars ( int  lev,
const amrex::Vector< std::string > &  plot_var_names,
const amrex::Vector< std::string > &  varnames,
const amrex::Vector< plotfile2d::Plotfile2DOutputDescriptor > &  output_descriptors,
amrex::MultiFab &  mf_dst,
int  ncomp_mf 
)
172 {
173  // Make sure getPgivenRTh and getTgivenRandRTh don't fail
174  if (check_for_nans) {
176  }
177 
178  int mf_comp = 0;
179 
180  // Set all components to zero in case they aren't defined below
181  mf_dst.setVal(0.0);
182 
183  // Expose domain khi and klo at each level
184  int klo = geom[lev].Domain().smallEnd(2);
185  int khi = geom[lev].Domain().bigEnd(2);
186 
187  const MultiFab* pblh_source = nullptr;
188  const MultiFab* sens_flux_source = SFS_hfx3_lev[lev].get();
189  const MultiFab* laten_flux_source = SFS_q1fx3_lev[lev].get();
190  const MultiFab* shoc_ustar_source = nullptr;
191  const MultiFab* shoc_olen_source = nullptr;
192  const MultiFab* shoc_wthv_source = nullptr;
193  const ShocDriver* native_shoc = native_shoc_driver[lev].get();
194  SurfaceLayer* surf_layer = m_SurfaceLayer[Orientation(Direction::z,Orientation::low)].get();
195  const bool native_shoc_owns_scalar_fluxes =
196  native_shoc && native_shoc->owns_scalar_surface_fluxes();
197  const bool native_shoc_has_consumed_flux_diagnostics =
198  native_shoc && native_shoc->has_consumed_surface_flux_diagnostics();
199  if (native_shoc && native_shoc->has_native_diagnostics()) {
200  pblh_source = &native_shoc->pblh_diagnostics();
201  }
202  // Native SHOC state_update clears the host SFS arrays after consuming
203  // them. Use SHOC's preserved snapshots only for flux components whose
204  // corresponding host SFS field existed; otherwise keep the source null
205  // so the 2D writer emits the documented -999 missing value.
207  native_shoc_owns_scalar_fluxes,
208  native_shoc_has_consumed_flux_diagnostics,
209  SFS_hfx3_lev[lev] != nullptr)) {
210  sens_flux_source = &native_shoc->consumed_sens_flux_diagnostics();
211  }
213  native_shoc_owns_scalar_fluxes,
214  native_shoc_has_consumed_flux_diagnostics,
215  SFS_q1fx3_lev[lev] != nullptr)) {
216  laten_flux_source = &native_shoc->consumed_laten_flux_diagnostics();
217  }
218  if (native_shoc && native_shoc->has_native_diagnostics()) {
219  shoc_ustar_source = &native_shoc->shoc_ustar_diagnostics();
220  shoc_olen_source = &native_shoc->shoc_olen_diagnostics();
221  shoc_wthv_source = &native_shoc->wthv_sec_diagnostics();
222  }
223  // pblh should follow the active PBL diagnostic provider. Native SHOC
224  // diagnoses its own PBL height in state_update mode; SurfaceLayer
225  // remains the fallback for non-SHOC configurations.
226  //
227  // NOTE: the SurfaceLayer pblh holds only the bogus value it was initialized
228  // with unless erf.most.pblh_calc was set (the default is "none"), so we
229  // leave the source null in that case and emit the missing value.
230  if (!pblh_source && surf_layer && surf_layer->computes_pblh()) {
231  pblh_source = surf_layer->get_pblh(lev);
232  }
233 
234  if (containerHasElement(plot_var_names, "z_surf")) {
235 #ifdef _OPENMP
236 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
237 #endif
238  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
239  {
240  const Box& bx = mfi.tilebox();
241  const Array4<Real>& derdat = mf_dst.array(mfi);
242  const Array4<const Real>& z_phys_arr = z_phys_nd[lev]->const_array(mfi);
243  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
244  derdat(i, j, k, mf_comp) = Compute_Z_AtWFace(i, j, 0, z_phys_arr);
245  });
246  }
247  mf_comp++;
248  }
249 
250  if (containerHasElement(plot_var_names, "landmask")) {
251 #ifdef _OPENMP
252 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
253 #endif
254  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
255  {
256  const Box& bx = mfi.tilebox();
257  const Array4<Real>& derdat = mf_dst.array(mfi);
258  const Array4<const int>& lmask_arr = lmask_lev[lev][0]->const_array(mfi);
259  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
260  derdat(i, j, k, mf_comp) = static_cast<Real>(lmask_arr(i, j, 0));
261  });
262  }
263  mf_comp++;
264  }
265 
266  if (containerHasElement(plot_var_names, "mapfac")) {
267 #ifdef _OPENMP
268 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
269 #endif
270  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
271  {
272  const Box& bx = mfi.tilebox();
273  const Array4<Real>& derdat = mf_dst.array(mfi);
274  const Array4<Real>& mf_m = mapfac[lev][MapFacType::m_x]->array(mfi);
275  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
276  derdat(i ,j ,k, mf_comp) = mf_m(i,j,0);
277  });
278  }
279  mf_comp++;
280  }
281 
282  if (containerHasElement(plot_var_names, "lat_m")) {
283  if (lat_m[lev]) {
284 #ifdef _OPENMP
285 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
286 #endif
287  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
288  {
289  const Box& bx = mfi.tilebox();
290  const Array4<Real>& derdat = mf_dst.array(mfi);
291  const Array4<Real>& data = lat_m[lev]->array(mfi);
292  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
293  derdat(i, j, k, mf_comp) = data(i,j,0);
294  });
295  }
296  }
297  mf_comp++;
298  } // lat_m
299 
300  if (containerHasElement(plot_var_names, "lon_m")) {
301  if (lon_m[lev]) {
302 #ifdef _OPENMP
303 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
304 #endif
305  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
306  {
307  const Box& bx = mfi.tilebox();
308  const Array4<Real>& derdat = mf_dst.array(mfi);
309  const Array4<Real>& data = lon_m[lev]->array(mfi);
310  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
311  derdat(i, j, k, mf_comp) = data(i,j,0);
312  });
313  }
314  } else {
315  mf_dst.setVal(0.0,mf_comp,1,0);
316  }
317 
318  mf_comp++;
319 
320  } // lon_m
321 
322  ///////////////////////////////////////////////////////////////////////
323  // Surface and single-level diagnostics use the fill helpers below.
324  // Column reductions and future interpolated surfaces keep explicit
325  // assembly paths until their contracts are isolated in dedicated
326  // helpers.
327  if (containerHasElement(plot_var_names, "u_star")) {
329  mf_dst, mf_comp, surf_layer ? surf_layer->get_u_star(lev) : nullptr,
330  0, -999);
331  mf_comp++;
332  } // u_star
333 
334  if (containerHasElement(plot_var_names, "w_star")) {
335  // NOTE: w_star holds only the bogus value it was initialized with unless
336  // erf.most.include_wstar is on (it is off by default), so we must
337  // pass a null source in that case and emit the missing value
338  const MultiFab* w_star_source = (surf_layer && surf_layer->computes_w_star())
339  ? surf_layer->get_w_star(lev) : nullptr;
341  mf_dst, mf_comp, w_star_source, 0, -999);
342  mf_comp++;
343  } // w_star
344 
345  if (containerHasElement(plot_var_names, "t_star")) {
347  mf_dst, mf_comp, surf_layer ? surf_layer->get_t_star(lev) : nullptr,
348  0, -999);
349  mf_comp++;
350  } // t_star
351 
352  if (containerHasElement(plot_var_names, "q_star")) {
354  mf_dst, mf_comp, surf_layer ? surf_layer->get_q_star(lev) : nullptr,
355  0, -999);
356  mf_comp++;
357  } // q_star
358 
359  if (containerHasElement(plot_var_names, "Olen")) {
361  mf_dst, mf_comp, surf_layer ? surf_layer->get_olen(lev) : nullptr,
362  0, -999);
363  mf_comp++;
364  } // Olen
365 
366  if (containerHasElement(plot_var_names, "pblh")) {
368  mf_dst, mf_comp, pblh_source, 0, -999);
369  mf_comp++;
370  } // pblh
371 
372  if (containerHasElement(plot_var_names, "t_surf")) {
374  mf_dst, mf_comp, surf_layer ? surf_layer->get_t_surf(lev) : nullptr,
375  0, -999);
376  mf_comp++;
377  } // t_surf
378 
379  if (containerHasElement(plot_var_names, "q_surf")) {
381  mf_dst, mf_comp, surf_layer ? surf_layer->get_q_surf(lev) : nullptr,
382  0, -999);
383  mf_comp++;
384  } // q_surf
385 
386  if (containerHasElement(plot_var_names, "z0")) {
388  mf_dst, mf_comp, surf_layer ? surf_layer->get_z0(lev) : nullptr,
389  0, -999);
390  mf_comp++;
391  } // z0
392 
393  if (containerHasElement(plot_var_names, "OLR")) {
394  // Outgoing longwave: the upward LW at the top-of-atmosphere
395  // interface, which rad_fluxes keeps in the z-ghost cell above the
396  // top layer (index khi holds the lower interface of the top layer).
398  mf_dst, mf_comp, rad_fluxes[lev].get(), khi + 1, -999, 2);
399  mf_comp++;
400  } // OLR
401 
402  if (containerHasElement(plot_var_names, "sens_flux")) {
404  mf_dst, mf_comp, sens_flux_source, klo, -999);
405  mf_comp++;
406  } // sens_flux
407 
408  // Keep the legacy output name "laten_flux"; it maps to the vertical
409  // water-vapor surface flux field.
410  if (containerHasElement(plot_var_names, "laten_flux")) {
412  mf_dst, mf_comp, laten_flux_source, klo, -999);
413  mf_comp++;
414  } // laten_flux
415 
416  if (containerHasElement(plot_var_names, "surf_pres")) {
417  bool moist = (solverChoice.moisture_type != MoistureType::None);
418 #ifdef _OPENMP
419 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
420 #endif
421  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
422  {
423  const Box& bx = mfi.tilebox();
424  const auto& derdat = mf_dst.array(mfi);
425  const auto& cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
426  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
427  auto rt = cons_arr(i,j,klo,RhoTheta_comp);
428  auto qv = (moist) ? cons_arr(i,j,klo,RhoQ1_comp)/cons_arr(i,j,klo,Rho_comp)
429  : zero;
430  derdat(i, j, k, mf_comp) = getPgivenRTh(rt, qv);
431  });
432  }
433  mf_comp++;
434  } // surf_pres
435 
436  if (containerHasElement(plot_var_names, "sea_level_pressure")) {
438  mf_dst, mf_comp, vars_new[lev][Vars::cons], *z_phys_nd[lev],
440  mf_comp++;
441  } // sea_level_pressure
442 
443  const auto precip_sources =
444  micro ? micro->Get_Surface_Precip_Accumulation_Ptrs(lev)
446  const auto selected_precipitation =
448  precip_sources);
449  if (selected_precipitation.n > 0) {
451  precip_sources,
452  selected_precipitation,
453  klo);
454  }
455 
456  if (containerHasElement(plot_var_names, "precip_total_accum")) { mf_comp++; }
457  if (containerHasElement(plot_var_names, "precip_rain_accum")) { mf_comp++; }
458  if (containerHasElement(plot_var_names, "precip_snow_accum")) { mf_comp++; }
459  if (containerHasElement(plot_var_names, "precip_graupel_accum")) { mf_comp++; }
460  if (containerHasElement(plot_var_names, "precip_hail_accum")) { mf_comp++; }
461  if (containerHasElement(plot_var_names, "precip_frozen_accum")) { mf_comp++; }
462 
463  if (containerHasElement(plot_var_names, "integrated_qv")) {
464  // integrated_qv remains the legacy column-reduction example.
465  // Scheme-aware condensed water paths use a dedicated helper below
466  // rather than the single-k copy helpers.
467  MultiFab mf_qv_int(mf_dst,make_alias,mf_comp,1);
468  if (solverChoice.moisture_type != MoistureType::None) {
469  volWgtColumnSum(lev, vars_new[lev][Vars::cons], RhoQ1_comp, mf_qv_int, *detJ_cc[lev]);
470  } else {
471  mf_qv_int.setVal(0.);
472  }
473  mf_comp++;
474  }
475 
476  const auto selected_water_paths =
478  if (selected_water_paths.n > 0) {
479  // Condensed water paths are scheme-aware column reductions. Their
480  // availability comes from solverChoice.moisture_indices, and the
481  // reduction uses the same metric convention as integrated_qv.
483  vars_new[lev][Vars::cons],
484  selected_water_paths,
485  geom[lev],
486  *detJ_cc[lev]);
487  }
488 
489  if (containerHasElement(plot_var_names, "integrated_qc")) { mf_comp++; }
490  if (containerHasElement(plot_var_names, "integrated_qi")) { mf_comp++; }
491  if (containerHasElement(plot_var_names, "integrated_qr")) { mf_comp++; }
492  if (containerHasElement(plot_var_names, "integrated_qs")) { mf_comp++; }
493  if (containerHasElement(plot_var_names, "integrated_qg")) { mf_comp++; }
494 
495  if (containerHasElement(plot_var_names, "surface_diagnostic_source")) {
497  mf_dst, mf_comp,
498  surf_layer ? surf_layer->get_surface_diagnostic_source(lev) : nullptr,
499  0, 0);
500  mf_comp++;
501  } // surface_diagnostic_source
502 
503  if (containerHasElement(plot_var_names, "sensible_heat_flux")) {
505  mf_dst, mf_comp, sens_flux_source, klo, -999);
506  mf_comp++;
507  } // sensible_heat_flux
508 
509  if (containerHasElement(plot_var_names, "latent_heat_flux")) {
511  mf_dst, mf_comp, laten_flux_source, klo, -999);
512  mf_comp++;
513  } // latent_heat_flux
514 
515  if (containerHasElement(plot_var_names, "shoc_u_star")) {
517  mf_dst, mf_comp, shoc_ustar_source, klo, -999);
518  mf_comp++;
519  } // shoc_u_star
520 
521  if (containerHasElement(plot_var_names, "shoc_Olen")) {
523  mf_dst, mf_comp, shoc_olen_source, klo, -999);
524  mf_comp++;
525  } // shoc_Olen
526 
527  if (containerHasElement(plot_var_names, "shoc_wthv_sfc")) {
529  mf_dst, mf_comp, shoc_wthv_source, klo, -999);
530  mf_comp++;
531  } // shoc_wthv_sfc
532 
533  // Land-surface provider fields use the generic LandSurface name/index
534  // interface. The catalog supplies metadata; this block only assembles
535  // stored values and translates provider sentinels.
536  int temperature_2m_comp = -1;
537  int mixing_ratio_2m_comp = -1;
538  int near_surface_source_comp = -1;
539  for (const auto& name : plot_var_names) {
541  if (!descriptor) {
543  }
544  if (!is_land_surface_diagnostic(descriptor)) {
545  continue;
546  }
547 
548  if (is_unified_near_surface_diagnostic(descriptor->id)) {
550  temperature_2m_comp = mf_comp;
552  mixing_ratio_2m_comp = mf_comp;
553  } else {
554  near_surface_source_comp = mf_comp;
555  }
556  ++mf_comp;
557  continue;
558  }
559 
561  mf_dst, mf_comp, lsm.Get_Data_Ptr(lev, name), 0, -999);
562  ++mf_comp;
563  }
564 
565  if (temperature_2m_comp >= 0 || mixing_ratio_2m_comp >= 0 ||
566  near_surface_source_comp >= 0) {
567  near_surface_diagnostics::Sources near_surface_sources;
568  near_surface_sources.native_temperature_vegetated =
569  lsm.Get_Data_Ptr(lev, "noahmp_temperature_2m_vegetated");
570  near_surface_sources.native_temperature_bare =
571  lsm.Get_Data_Ptr(lev, "noahmp_temperature_2m_bare");
572  near_surface_sources.native_mixing_ratio_vegetated =
573  lsm.Get_Data_Ptr(lev, "noahmp_water_vapor_mixing_ratio_2m_vegetated");
574  near_surface_sources.native_mixing_ratio_bare =
575  lsm.Get_Data_Ptr(lev, "noahmp_water_vapor_mixing_ratio_2m_bare");
576  near_surface_sources.native_vegetation_fraction =
577  lsm.Get_Data_Ptr(lev, "noahmp_vegetation_fraction");
578  near_surface_sources.theta_surface =
579  surf_layer ? surf_layer->get_t_surf(lev) : nullptr;
580  near_surface_sources.theta_star =
581  surf_layer ? surf_layer->get_t_star(lev) : nullptr;
582  near_surface_sources.mixing_ratio_surface =
583  surf_layer ? surf_layer->get_q_surf(lev) : nullptr;
584  near_surface_sources.mixing_ratio_star =
585  surf_layer ? surf_layer->get_q_star(lev) : nullptr;
586  near_surface_sources.roughness_height =
587  surf_layer ? surf_layer->get_z0(lev) : nullptr;
588  near_surface_sources.obukhov_length =
589  surf_layer ? surf_layer->get_olen(lev) : nullptr;
590  near_surface_sources.source_mask =
591  surf_layer ? surf_layer->get_surface_diagnostic_source(lev) : nullptr;
592  near_surface_sources.land_mask =
593  (lmask_lev[lev].empty()) ? nullptr : lmask_lev[lev][0].get();
594  near_surface_sources.cons = &vars_new[lev][Vars::cons];
595  near_surface_sources.z_phys_nd = z_phys_nd[lev].get();
596  near_surface_sources.dz = geom[lev].CellSize(2);
597  near_surface_sources.klo = klo;
598  near_surface_sources.moist = solverChoice.moisture_type != MoistureType::None;
599  near_surface_sources.has_lsm =
600  near_surface_sources.native_temperature_vegetated != nullptr &&
601  near_surface_sources.native_temperature_bare != nullptr &&
602  near_surface_sources.native_mixing_ratio_vegetated != nullptr &&
603  near_surface_sources.native_mixing_ratio_bare != nullptr &&
604  near_surface_sources.native_vegetation_fraction != nullptr;
605  near_surface_diagnostics::fill(mf_dst, temperature_2m_comp,
606  mixing_ratio_2m_comp,
607  near_surface_source_comp,
608  near_surface_sources);
609  }
610 
611  const int static_output_count = static_cast<int>(plot_var_names.size());
612  // If this level is anelastic then the base state pressure -- not the compressible
613  // EOS -- defines the pressure, both for the sampled "pressure" field and for
614  // the pressure vertical coordinate. This matches the 3-D plotfile path.
615  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp, 1);
616  const MultiFab* p_hse_ptr = (solverChoice.anelastic[lev] == 1) ? &p_hse : nullptr;
617  for (int out_idx = static_output_count; out_idx < static_cast<int>(output_descriptors.size()); ++out_idx) {
618  const auto& descriptor = output_descriptors[out_idx];
619  // Sampled-level descriptors are dynamic. The interpolator owns
620  // their vertical sampling so the writer remains an output
621  // assembly layer.
622  plotfile2d::SampledWindSources wind_sources;
623  wind_sources.xvel = &vars_new[lev][Vars::xvel];
624  wind_sources.yvel = &vars_new[lev][Vars::yvel];
625  wind_sources.zvel = &vars_new[lev][Vars::zvel];
627  mf_dst, mf_comp, descriptor,
628  vars_new[lev][Vars::cons],
629  z_phys_cc[lev].get(),
630  *z_phys_nd[lev],
631  z_phys_cc[lev] != nullptr,
633  klo, khi,
634  wind_sources,
635  p_hse_ptr);
636  mf_comp++;
637  }
638 
639  if (mf_comp != ncomp_mf) {
640  Abort(plotfile2d::format_2d_component_count_error(lev, mf_comp, ncomp_mf));
641  }
642 }
DiagnosticDescriptor descriptor
Definition: ERF_Plotfile2DCatalog.cpp:104
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:729
Definition: ERF_ShocDriver.H:40
bool has_consumed_surface_flux_diagnostics() const
Definition: ERF_ShocDriver.H:68
const amrex::MultiFab & consumed_laten_flux_diagnostics() const
Definition: ERF_ShocDriver.H:80
bool owns_scalar_surface_fluxes() const
Definition: ERF_ShocDriver.cpp:871
bool has_native_diagnostics() const
Definition: ERF_ShocDriver.H:67
const amrex::MultiFab & shoc_olen_diagnostics() const
Definition: ERF_ShocDriver.H:85
const amrex::MultiFab & shoc_ustar_diagnostics() const
Definition: ERF_ShocDriver.H:84
const amrex::MultiFab & consumed_sens_flux_diagnostics() const
Definition: ERF_ShocDriver.H:76
const amrex::MultiFab & pblh_diagnostics() const
Definition: ERF_ShocDriver.H:74
const amrex::MultiFab & wthv_sec_diagnostics() const
Definition: ERF_ShocDriver.H:92
Definition: ERF_SurfaceLayer.H:59
amrex::MultiFab * get_q_surf(const int &lev)
Definition: ERF_SurfaceLayer.H:1258
amrex::MultiFab * get_w_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1136
amrex::MultiFab * get_olen(const int &lev)
Definition: ERF_SurfaceLayer.H:1169
bool computes_w_star() const
Definition: ERF_SurfaceLayer.H:1142
amrex::MultiFab * get_u_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1129
amrex::MultiFab * get_surface_diagnostic_source(const int &lev)
Definition: ERF_SurfaceLayer.H:1273
amrex::MultiFab * get_t_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1155
amrex::MultiFab * get_q_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1162
amrex::MultiFab * get_pblh(const int &lev)
Definition: ERF_SurfaceLayer.H:1176
bool computes_pblh() const
Definition: ERF_SurfaceLayer.H:1148
amrex::MultiFab * get_z0(const int &lev)
Definition: ERF_SurfaceLayer.H:1287
amrex::MultiFab * get_t_surf(const int &lev)
Definition: ERF_SurfaceLayer.H:1243
void fill(MultiFab &dst, int temperature_comp, int mixing_ratio_comp, int source_comp, const Sources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:41
void fill_condensed_water_paths(MultiFab &dst, const MultiFab &cons, const SelectedWaterPathComponents &selected, const Geometry &geom, const MultiFab &detJ)
Definition: ERF_Plotfile2DWaterPath.cpp:240
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:107
void fill_sampled_level_component(MultiFab &dst, int dst_comp, const Plotfile2DOutputDescriptor &descriptor, const MultiFab &cons, const MultiFab *z_phys_cc, const MultiFab &z_phys_nd, bool have_z_phys_cc, const MoistureComponentIndices &moisture_indices, int klo, int khi, const SampledWindSources &wind_sources, const MultiFab *p_hse)
Definition: ERF_Plotfile2DInterpolator.cpp:172
SelectedWaterPathComponents selected_condensed_water_path_components(const amrex::Vector< std::string > &plot_var_names, const SolverChoice &solver_choice)
Definition: ERF_Plotfile2DWaterPath.cpp:215
std::string format_2d_component_count_error(int lev, int filled, int expected)
Definition: ERF_Plotfile2DUtils.cpp:89
const DiagnosticDescriptor * find_dynamic_soil_diagnostic(const std::string &name)
Definition: ERF_Plotfile2DCatalog.cpp:212
void fill_land_surface_component_from_klevel_or_missing(MultiFab &dst, int dst_comp, const MultiFab *src, int src_k, Real missing_value)
Definition: ERF_Plotfile2DFill.cpp:79
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:64
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:23
SelectedSurfacePrecipAccumulationComponents selected_precipitation_accumulation_components(const amrex::Vector< std::string > &plot_var_names, const SurfacePrecipAccumulationSources &sources)
Definition: ERF_Plotfile2DPrecip.cpp:98
void fill_precipitation_accumulations(MultiFab &dst, const SurfacePrecipAccumulationSources &sources, const SelectedSurfacePrecipAccumulationComponents &selected, const int klo)
Definition: ERF_Plotfile2DPrecip.cpp:140
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:138
const DiagnosticDescriptor * find_diagnostic(const std::string &name)
Definition: ERF_Plotfile2DCatalog.cpp:169
void fill_sea_level_pressure(MultiFab &destination, int destination_component, const MultiFab &conserved_state, const MultiFab &physical_nodal_height, int vapor_component, int klo)
Definition: ERF_SeaLevelPressure.cpp:14
Definition: ERF_NearSurfaceDiagnostics.H:166
const amrex::MultiFab * theta_surface
Definition: ERF_NearSurfaceDiagnostics.H:173
const amrex::MultiFab * z_phys_nd
Definition: ERF_NearSurfaceDiagnostics.H:182
bool moist
Definition: ERF_NearSurfaceDiagnostics.H:186
bool has_lsm
Definition: ERF_NearSurfaceDiagnostics.H:187
int klo
Definition: ERF_NearSurfaceDiagnostics.H:185
const amrex::iMultiFab * land_mask
Definition: ERF_NearSurfaceDiagnostics.H:180
const amrex::MultiFab * obukhov_length
Definition: ERF_NearSurfaceDiagnostics.H:178
const amrex::MultiFab * roughness_height
Definition: ERF_NearSurfaceDiagnostics.H:177
const amrex::MultiFab * native_temperature_bare
Definition: ERF_NearSurfaceDiagnostics.H:168
const amrex::MultiFab * cons
Definition: ERF_NearSurfaceDiagnostics.H:181
const amrex::MultiFab * theta_star
Definition: ERF_NearSurfaceDiagnostics.H:174
const amrex::MultiFab * native_temperature_vegetated
Definition: ERF_NearSurfaceDiagnostics.H:167
const amrex::MultiFab * mixing_ratio_surface
Definition: ERF_NearSurfaceDiagnostics.H:175
const amrex::MultiFab * native_mixing_ratio_bare
Definition: ERF_NearSurfaceDiagnostics.H:170
amrex::Real dz
Definition: ERF_NearSurfaceDiagnostics.H:184
const amrex::MultiFab * source_mask
Definition: ERF_NearSurfaceDiagnostics.H:179
const amrex::MultiFab * mixing_ratio_star
Definition: ERF_NearSurfaceDiagnostics.H:176
const amrex::MultiFab * native_mixing_ratio_vegetated
Definition: ERF_NearSurfaceDiagnostics.H:169
const amrex::MultiFab * native_vegetation_fraction
Definition: ERF_NearSurfaceDiagnostics.H:171
Definition: ERF_Plotfile2DInterpolator.H:28
const amrex::MultiFab * xvel
Definition: ERF_Plotfile2DInterpolator.H:29
const amrex::MultiFab * zvel
Definition: ERF_Plotfile2DInterpolator.H:31
const amrex::MultiFab * yvel
Definition: ERF_Plotfile2DInterpolator.H:30
Here is the call graph for this function:

◆ FillPlot3DVars()

void ERF::FillPlot3DVars ( int  lev,
const amrex::Vector< std::string > &  plot_var_names,
Plot3DScratch scratch,
amrex::MultiFab &  mf_dst,
int  ncomp_mf,
amrex::Real  tnew 
)
503 {
504  auto& mf_cc_vel = scratch.mf_cc_vel;
505  auto& mf_cc_tau = scratch.mf_cc_tau;
506  auto& mf_cc_fx = scratch.mf_cc_fx;
507 
508  // Make sure getPgivenRTh and getTgivenRandRTh don't fail
509  if (check_for_nans) {
511  }
512 
513  int mf_comp = 0;
514  const int ncomp_cons_lev = vars_new[lev][Vars::cons].nComp();
515 
516  // The moisture map is the authority on which moisture variables exist;
517  // this only re-checks that the components it names are inside the state
518  // that was actually allocated, which would be a registration bug.
520  auto assert_comps_in_state =
521  [&](const char* /*field*/, const MoistureComponentIndices::CompList& list)
522  {
524  !list.empty(), "aggregate 3D plot variable requires at least one moist species");
525  for (int n = 0; n < list.size; ++n) {
527  list.comp[n] >= 0 && list.comp[n] < ncomp_cons_lev,
528  "aggregate 3D plot variable names a moist component outside the state");
529  }
530  };
531 
532  // Sum a moisture component list into the next plot component as a mixing
533  // ratio: the state holds rho-weighted species, so the sum is divided by
534  // density on the way out.
535  auto copy_moist_sum = [&](const MoistureComponentIndices::CompList& list)
536  {
537  MultiFab::Copy(mf_dst, vars_new[lev][Vars::cons], list.comp[0], mf_comp, 1, 0);
538  for (int n = 1; n < list.size; ++n) {
539  MultiFab::Add(mf_dst, vars_new[lev][Vars::cons], list.comp[n], mf_comp, 1, 0);
540  }
541  MultiFab::Divide(mf_dst, vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
542  mf_comp += 1;
543  };
544 
545  // Copy one moist species out of the state as a mixing ratio.
546  auto copy_moist_species = [&](const std::string& name)
547  {
548  const int comp = mi.comp_for_var(name);
549  if (containerHasElement(plot_var_names, name) && (comp >= 0)) {
550  AMREX_ALWAYS_ASSERT(comp < ncomp_cons_lev);
551  MultiFab::Copy( mf_dst, vars_new[lev][Vars::cons], comp , mf_comp, 1, 0);
552  MultiFab::Divide(mf_dst, vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
553  mf_comp += 1;
554  }
555  };
556 
557  // Copy one moist diagnostic out of the scheme's qmoist arrays.
558  auto copy_moist_diagnostic = [&](const std::string& name)
559  {
560  const int idx = mi.qmoist_index_for_var(name);
561  if (containerHasElement(plot_var_names, name) && (idx >= 0)) {
562  AMREX_ALWAYS_ASSERT(idx < static_cast<int>(qmoist[lev].size()) &&
563  qmoist[lev][idx] != nullptr);
564  MultiFab::Copy(mf_dst, *(qmoist[lev][idx]), 0, mf_comp, 1, 0);
565  mf_comp += 1;
566  }
567  };
568 
569  BoxArray ba(vars_new[lev][Vars::cons].boxArray());
570  DistributionMapping dm = vars_new[lev][Vars::cons].DistributionMap();
571 
572  // First, copy any of the conserved state variables into the output plotfile
573  for (int i = 0; i < cons_names.size(); ++i) {
574  if (containerHasElement(plot_var_names, cons_names[i])) {
576  AMREX_ALWAYS_ASSERT(cons_comp >= 0 && cons_comp < vars_new[lev][Vars::cons].nComp());
577  MultiFab::Copy(mf_dst,vars_new[lev][Vars::cons],cons_comp,mf_comp,1,0);
578  mf_comp++;
579  }
580  }
581 
582  // Next, check for velocities
583  if (containerHasElement(plot_var_names, "x_velocity")) {
584  MultiFab::Copy(mf_dst, mf_cc_vel[lev], 0, mf_comp, 1, 0);
585  mf_comp += 1;
586  }
587  if (containerHasElement(plot_var_names, "y_velocity")) {
588  MultiFab::Copy(mf_dst, mf_cc_vel[lev], 1, mf_comp, 1, 0);
589  mf_comp += 1;
590  }
591  if (containerHasElement(plot_var_names, "z_velocity")) {
592  MultiFab::Copy(mf_dst, mf_cc_vel[lev], 2, mf_comp, 1, 0);
593  mf_comp += 1;
594  }
595 
596  // Create multifabs for HSE and pressure fields used to derive other quantities
597  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp , 1);
598  MultiFab p_hse(base_state[lev], make_alias, BaseState::p0_comp , 1);
599  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
600  MultiFab pi_hse(base_state[lev], make_alias, BaseState::pi0_comp, 1);
601  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp, 1);
602 
603  MultiFab pressure;
604 
605  const bool needs_pressure = erf_plotfile::plot3d_needs_pressure(plot_var_names);
606  if (solverChoice.anelastic[lev] == 0) {
607  if (needs_pressure) {
608  int ng = (containerHasElement(plot_var_names, "dpdx") || containerHasElement(plot_var_names, "dpdy") ||
609  containerHasElement(plot_var_names, "dpdz")) ? 1 : 0;
610 
611  // Allocate space for pressure
612  pressure.define(ba,dm,1,ng);
613 
614  if (ng > 0) {
615  // Default to p_hse as a way of filling ghost cells at domain boundaries
616  MultiFab::Copy(pressure,p_hse,0,0,1,1);
617  }
618 #ifdef _OPENMP
619 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
620 #endif
621  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
622  {
623  const Box& gbx = mfi.growntilebox(IntVect(ng,ng,0));
624 
625  const Array4<Real >& p_arr = pressure.array(mfi);
626  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
627  const int qv_comp = mi.qv;
628 
629  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
630  {
631  Real qv_for_p = (qv_comp >= 0) ? S_arr(i,j,k,qv_comp)/S_arr(i,j,k,Rho_comp) : 0;
632  const Real rhotheta = S_arr(i,j,k,RhoTheta_comp);
633  p_arr(i, j, k) = getPgivenRTh(rhotheta,qv_for_p);
634  });
635  } // mfi
636  pressure.FillBoundary(geom[lev].periodicity());
637  } // compute compressible pressure
638  } // not anelastic
639  else {
640  if (needs_pressure) {
641  // Copy p_hse into pressure if using anelastic
642  pressure.define(ba,dm,1,0);
643  MultiFab::Copy(pressure,p_hse,0,0,1,0);
644  }
645  }
646 
647  // ***************************************************************************************
648  // Finally, check for any derived quantities and compute them, inserting
649  // them into our output multifab
650  // ***************************************************************************************
651  auto calculate_derived = [&](const std::string& der_name,
652  MultiFab& src_mf,
653  decltype(derived::erf_dernull)& der_function)
654  {
655  if (containerHasElement(plot_var_names, der_name)) {
656  MultiFab dmf(mf_dst, make_alias, mf_comp, 1);
657  //
658  // NOTE: we must not tile in z here because some of the derived quantities
659  // ("precipitable", "mucape", "helicity", "max_reflectivity") are
660  // whole-column operations and require the full column in each box
661  //
662 #ifdef _OPENMP
663 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
664 #endif
665  for (MFIter mfi(dmf, TileNoZ()); mfi.isValid(); ++mfi)
666  {
667  const Box& bx = mfi.tilebox();
668  auto& dfab = dmf[mfi];
669  auto& sfab = src_mf[mfi];
670  auto& zfab = (*z_phys_cc[lev])[mfi];
671  der_function(bx, dfab, 0, 1, sfab, zfab, Geom(lev), tnew, nullptr, lev);
672  }
673 
674  mf_comp++;
675  }
676  }; // end calculate_derived
677  // ***************************************************************************************
678 
679  // *****************************************************************************************
680  // NOTE: All derived variables computed below **MUST MATCH THE ORDER** of "derived_names"
681  // defined in ERF.H
682  // *****************************************************************************************
683 
684  if (solverChoice.anelastic[lev]) {
685  if (containerHasElement(plot_var_names, "temp")) {
686  MultiFab dmf(mf_dst, make_alias, mf_comp, 1);
687  const Real rdOcp = solverChoice.rdOcp;
688 #ifdef _OPENMP
689 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
690 #endif
691  for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
692  const Box& bx = mfi.tilebox();
693  const auto temp = dmf.array(mfi);
694  const auto state = vars_new[lev][Vars::cons].const_array(mfi);
695  const auto p0 = p_hse.const_array(mfi);
696  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
697  const Real theta = state(i,j,k,RhoTheta_comp) /
698  state(i,j,k,Rho_comp);
699  temp(i,j,k,0) = getTgivenPandTh(p0(i,j,k), theta, rdOcp);
700  });
701  }
702  ++mf_comp;
703  }
704  } else if (mi.has_moisture()) {
705  calculate_derived("temp", vars_new[lev][Vars::cons], derived::erf_dermoisttemp);
706  } else {
707  calculate_derived("temp", vars_new[lev][Vars::cons], derived::erf_dertemp);
708  }
709  calculate_derived("theta", vars_new[lev][Vars::cons], derived::erf_dertheta);
710  calculate_derived("KE", vars_new[lev][Vars::cons], derived::erf_derKE);
711  calculate_derived("scalar", vars_new[lev][Vars::cons], derived::erf_derscalar);
712  calculate_derived("soundspeed", vars_new[lev][Vars::cons], derived::erf_dersoundspeed);
713 
714  if (containerHasElement(plot_var_names, "reflectivity"))
715  {
716  // NOTE: this tests the data layout the reflectivity kernel assumes rather
717  // than enumerating moisture models (see ERF_Tagging.cpp)
719  calculate_derived("reflectivity", vars_new[lev][Vars::cons], derived::erf_derreflectivity);
720  } else {
721  mf_dst.setVal(zero, mf_comp, 1, 0);
722  mf_comp++;
723  }
724  }
725 
726  if (containerHasElement(plot_var_names, "max_reflectivity"))
727  {
728  // NOTE: this tests the data layout the reflectivity kernel assumes rather
729  // than enumerating moisture models (see ERF_Tagging.cpp)
731  calculate_derived("max_reflectivity", vars_new[lev][Vars::cons], derived::erf_dermaxreflectivity);
732  } else {
733  mf_dst.setVal(zero, mf_comp, 1, 0);
734  mf_comp++;
735  }
736  }
737 
738  // Precipitable water is a column integral of vapor, so it needs a scheme
739  // that carries vapor -- which is what has_moisture() tests.
740  if (mi.has_moisture()) {
741  calculate_derived("precipitable" , vars_new[lev][Vars::cons], derived::erf_derprecipitable);
742  }
743  calculate_derived("mucape" , vars_new[lev][Vars::cons], derived::erf_dermucape);
744 
745  calculate_derived("vorticity_x", mf_cc_vel[lev], derived::erf_dervortx);
746  calculate_derived("vorticity_y", mf_cc_vel[lev], derived::erf_dervorty);
747  calculate_derived("vorticity_z", mf_cc_vel[lev], derived::erf_dervortz);
748  calculate_derived("helicity", mf_cc_vel[lev], derived::erf_derhelicity);
749  calculate_derived("local_helicity", mf_cc_vel[lev], derived::erf_derlocalhelicity);
750  calculate_derived("vort_stretching", mf_cc_vel[lev], derived::erf_dervortstretching);
751  calculate_derived("magvel", mf_cc_vel[lev], derived::erf_dermagvel);
752 
753  if (containerHasElement(plot_var_names, "divU"))
754  {
755  MultiFab dmf(mf_dst, make_alias, mf_comp, 1);
756  Array<MultiFab const*, AMREX_SPACEDIM> u;
757  u[0] = &(vars_new[lev][Vars::xvel]);
758  u[1] = &(vars_new[lev][Vars::yvel]);
759  u[2] = &(vars_new[lev][Vars::zvel]);
760  compute_divergence(lev, dmf, u, *mapfac[lev][MapFacType::m_x],
762  *mapfac[lev][MapFacType::u_y], *ax[lev], *ay[lev],
763  *detJ_cc[lev], geom[lev]);
764  mf_comp += 1;
765  }
766 
767  if (containerHasElement(plot_var_names, "pres_hse"))
768  {
769  MultiFab::Copy(mf_dst,p_hse,0,mf_comp,1,0);
770  mf_comp += 1;
771  }
772  if (containerHasElement(plot_var_names, "dens_hse"))
773  {
774  MultiFab::Copy(mf_dst,r_hse,0,mf_comp,1,0);
775  mf_comp += 1;
776  }
777  if (containerHasElement(plot_var_names, "theta_hse"))
778  {
779  MultiFab::Copy(mf_dst,th_hse,0,mf_comp,1,0);
780  mf_comp += 1;
781  }
782  if (containerHasElement(plot_var_names, "pi_hse"))
783  {
784  MultiFab::Copy(mf_dst,pi_hse,0,mf_comp,1,0);
785  mf_comp += 1;
786  }
787  if (containerHasElement(plot_var_names, "qv_hse"))
788  {
789  MultiFab::Copy(mf_dst,qv_hse,0,mf_comp,1,0);
790  mf_comp += 1;
791  }
792 
793  if (containerHasElement(plot_var_names, "pressure"))
794  {
795  if (solverChoice.anelastic[lev] == 1) {
796  MultiFab::Copy(mf_dst, p_hse, 0, mf_comp, 1, 0);
797  } else {
798  MultiFab::Copy(mf_dst, pressure, 0, mf_comp, 1, 0);
799  }
800 
801  mf_comp += 1;
802  }
803 
804  if (containerHasElement(plot_var_names, "pert_pres"))
805  {
806  if (solverChoice.anelastic[lev] == 1) {
807  MultiFab::Copy(mf_dst, pp_inc[lev], 0, mf_comp, 1, 0);
808  } else {
809  MultiFab::Copy(mf_dst, pressure, 0, mf_comp, 1, 0);
810  MultiFab::Subtract(mf_dst,p_hse,0,mf_comp,1,IntVect{0});
811  }
812  mf_comp += 1;
813  }
814 
815  if (containerHasElement(plot_var_names, "pert_dens"))
816  {
817 #ifdef _OPENMP
818 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
819 #endif
820  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
821  {
822  const Box& bx = mfi.tilebox();
823  const Array4<Real>& derdat = mf_dst.array(mfi);
824  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
825  const Array4<Real const>& r0_arr = r_hse.const_array(mfi);
826  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
827  derdat(i, j, k, mf_comp) = S_arr(i,j,k,Rho_comp) - r0_arr(i,j,k);
828  });
829  }
830  mf_comp ++;
831  }
832 
833  if (containerHasElement(plot_var_names, "buoyancy"))
834  {
835  MultiFab qt(mf_dst.boxArray(), mf_dst.DistributionMap(), 1, 1);
836  MultiFab b(mf_dst.boxArray(), mf_dst.DistributionMap(), 1, 0);
837  MultiFab S_prim(mf_dst.boxArray(), mf_dst.DistributionMap(),
838  vars_new[lev][Vars::cons].nComp()-1, 1);
839 
840  qt.setVal(0.);
841  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
842  if (solverChoice.moisture_type != MoistureType::None) {
843  make_qt(vars_new[lev][Vars::cons], qt, n_qstate_into_total);
844  }
845  //
846  // NOTE: we must fill one ghost cell of S_prim here because make_buoyancy
847  // reads cell_prim(i,j,k-1) at the lower z face of every box -- with
848  // ng = 0 those ghost cells hold uninitialized data at the bottom of
849  // any box that doesn't touch the bottom of the domain. (The ghost
850  // cells of vars_new[cons] are valid since we fillpatched above.)
851  //
852  cons_to_prim(vars_new[lev][Vars::cons], S_prim, 1);
853 
854  b.setVal(0.); // Need to initialize to zero because buoyancy not defined on faces at top and bottom of domain
855  make_buoyancy(lev, vars_new[lev], S_prim, qt, b, geom[lev], solverChoice, base_state[lev], n_qstate_into_total,
856  get_eb(lev), solverChoice.anelastic[lev]);
857  MultiFab::Copy(mf_dst, b, 0, mf_comp, 1, 0);
858  mf_comp ++;
859  }
860 
861  if (containerHasElement(plot_var_names, "eq_pot_temp"))
862  {
863 #ifdef _OPENMP
864 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
865 #endif
866  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
867  {
868  const Box& bx = mfi.tilebox();
869  const Array4<Real>& derdat = mf_dst.array(mfi);
870  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
871  const Array4<Real const>& p_arr = pressure.const_array(mfi);
872  const int qv_comp = mi.qv;
873  const int qc_comp = mi.qc;
874  const bool anelastic = solverChoice.anelastic[lev];
875  const Real rdOcp = solverChoice.rdOcp;
876  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
877  Real qv = (qv_comp >= 0) ? S_arr(i,j,k,qv_comp)/S_arr(i,j,k,Rho_comp) : zero;
878  Real qc = (qc_comp >= 0) ? S_arr(i,j,k,qc_comp)/S_arr(i,j,k,Rho_comp) : zero;
879  Real T = anelastic ?
880  getTgivenPandTh(p_arr(i,j,k), S_arr(i,j,k,RhoTheta_comp) /
881  S_arr(i,j,k,Rho_comp), rdOcp) :
882  getTgivenRandRTh(S_arr(i,j,k,Rho_comp), S_arr(i,j,k,RhoTheta_comp), qv);
883  Real fac = Cp_d + Cp_l*(qv + qc);
884  Real pv = erf_esatw(T)*Real(100.0);
885 
886  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)) ;
887  });
888  }
889  mf_comp ++;
890  }
891 
892  if (containerHasElement(plot_var_names, "VPD"))
893  {
894 #ifdef _OPENMP
895 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
896 #endif
897  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
898  {
899  const Box& bx = mfi.tilebox();
900  const Array4<Real>& derdat = mf_dst.array(mfi);
901  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
902  const Array4<Real const>& p_arr = pressure.const_array(mfi);
903  const int qv_comp = mi.qv;
904  const bool anelastic = solverChoice.anelastic[lev];
905  const Real rdOcp = solverChoice.rdOcp;
906  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
907  {
908  const Real qv = (qv_comp >= 0) ? S_arr(i,j,k,qv_comp)/S_arr(i,j,k,Rho_comp) : zero;
909 
910  const Real T = anelastic ?
911  getTgivenPandTh(p_arr(i,j,k), S_arr(i,j,k,RhoTheta_comp) /
912  S_arr(i,j,k,Rho_comp), rdOcp) :
913  getTgivenRandRTh(S_arr(i,j,k,Rho_comp), S_arr(i,j,k,RhoTheta_comp), qv);
914  const Real e_sat = Real(100.0) * erf_esatw_cc(T);
915 
916  const Real P = p_arr(i,j,k);
917  const Real e_act = P * qv / (RdoRv + qv);
918 
919  derdat(i,j,k,mf_comp) = std::max(amrex::Real(0), e_sat - e_act) * Real(0.001);
920  });
921  }
922  mf_comp ++;
923  }
924 
925 #ifdef ERF_USE_WINDFARM
926  if ( containerHasElement(plot_var_names, "num_turb") and
927  (solverChoice.windfarm_type == WindFarmType::Fitch or solverChoice.windfarm_type == WindFarmType::EWP or
928  solverChoice.windfarm_type == WindFarmType::SimpleAD or solverChoice.windfarm_type == WindFarmType::GeneralAD) )
929  {
930  MultiFab::Copy(mf_dst,Nturb[lev],0,mf_comp,1,0);
931  mf_comp ++;
932  }
933 
934  if ( containerHasElement(plot_var_names, "SMark0") and
935  (solverChoice.windfarm_type == WindFarmType::Fitch or solverChoice.windfarm_type == WindFarmType::EWP or
936  solverChoice.windfarm_type == WindFarmType::SimpleAD or solverChoice.windfarm_type == WindFarmType::GeneralAD) )
937  {
938  MultiFab::Copy(mf_dst,SMark[lev],0,mf_comp,1,0);
939  mf_comp ++;
940  }
941 
942  if (containerHasElement(plot_var_names, "SMark1") and
943  (solverChoice.windfarm_type == WindFarmType::SimpleAD or solverChoice.windfarm_type == WindFarmType::GeneralAD))
944  {
945  MultiFab::Copy(mf_dst,SMark[lev],1,mf_comp,1,0);
946  mf_comp ++;
947  }
948 #endif
949 
950  // **********************************************************************************************
951  // Allocate space if we are computing any pressure gradients
952  // **********************************************************************************************
953 
954  Vector<MultiFab> gradp_temp; gradp_temp.resize(AMREX_SPACEDIM);
955  if (containerHasElement(plot_var_names, "dpdx") ||
956  containerHasElement(plot_var_names, "dpdy") ||
957  containerHasElement(plot_var_names, "dpdz") ||
958  containerHasElement(plot_var_names, "pres_hse_x") ||
959  containerHasElement(plot_var_names, "pres_hse_y"))
960  {
961  gradp_temp[GpVars::gpx].define(convert(ba, IntVect(1,0,0)), dm, 1, 1); gradp_temp[GpVars::gpx].setVal(0.);
962  gradp_temp[GpVars::gpy].define(convert(ba, IntVect(0,1,0)), dm, 1, 1); gradp_temp[GpVars::gpy].setVal(0.);
963  gradp_temp[GpVars::gpz].define(convert(ba, IntVect(0,0,1)), dm, 1, 1); gradp_temp[GpVars::gpz].setVal(0.);
964  }
965 
966  // **********************************************************************************************
967  // These are based on computing gradient of full pressure
968  // **********************************************************************************************
969 
970  if (solverChoice.anelastic[lev] == 0) {
971  if ( (containerHasElement(plot_var_names, "dpdx")) ||
972  (containerHasElement(plot_var_names, "dpdy")) ||
973  (containerHasElement(plot_var_names, "dpdz")) ) {
974  compute_gradp(pressure, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
975  get_eb(lev), gradp_temp, solverChoice);
976  }
977  }
978 
979  if (containerHasElement(plot_var_names, "dpdx"))
980  {
981  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
982  {
983  const Box& bx = mfi.tilebox();
984  const Array4<Real >& derdat = mf_dst.array(mfi);
985  const Array4<Real const>& gpx_arr = (solverChoice.anelastic[lev] == 1) ?
986  gradp[lev][GpVars::gpx].array(mfi) : gradp_temp[GpVars::gpx].array(mfi);
987  const Array4<Real const>& mf_mx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
988  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
989  derdat(i ,j ,k, mf_comp) = myhalf * (gpx_arr(i+1,j,k) + gpx_arr(i,j,k)) * mf_mx_arr(i,j,0);
990  });
991  }
992  mf_comp ++;
993  } // dpdx
994  if (containerHasElement(plot_var_names, "dpdy"))
995  {
996  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
997  {
998  const Box& bx = mfi.tilebox();
999  const Array4<Real >& derdat = mf_dst.array(mfi);
1000  const Array4<Real const>& gpy_arr = (solverChoice.anelastic[lev] == 1) ?
1001  gradp[lev][GpVars::gpy].array(mfi) : gradp_temp[GpVars::gpy].array(mfi);
1002  const Array4<Real const>& mf_my_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
1003  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1004  derdat(i ,j ,k, mf_comp) = myhalf * (gpy_arr(i,j+1,k) + gpy_arr(i,j,k)) * mf_my_arr(i,j,0);
1005  });
1006  }
1007  mf_comp ++;
1008  } // dpdy
1009  if (containerHasElement(plot_var_names, "dpdz"))
1010  {
1011  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1012  {
1013  const Box& bx = mfi.tilebox();
1014  const Array4<Real >& derdat = mf_dst.array(mfi);
1015  const Array4<Real const>& gpz_arr = (solverChoice.anelastic[lev] == 1) ?
1016  gradp[lev][GpVars::gpz].array(mfi) : gradp_temp[GpVars::gpz].array(mfi);
1017  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1018  derdat(i ,j ,k, mf_comp) = myhalf * (gpz_arr(i,j,k+1) + gpz_arr(i,j,k));
1019  });
1020  }
1021  mf_comp ++;
1022  } // dpdz
1023 
1024  // **********************************************************************************************
1025  // These are based on computing gradient of basestate pressure
1026  // **********************************************************************************************
1027 
1028  if ( (containerHasElement(plot_var_names, "pres_hse_x")) ||
1029  (containerHasElement(plot_var_names, "pres_hse_y")) ) {
1030  compute_gradp(p_hse, geom[lev], *z_phys_nd[lev].get(), *z_phys_cc[lev].get(), mapfac[lev],
1031  get_eb(lev), gradp_temp, solverChoice);
1032  }
1033 
1034  if (containerHasElement(plot_var_names, "pres_hse_x"))
1035  {
1036  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1037  {
1038  const Box& bx = mfi.tilebox();
1039  const Array4<Real >& derdat = mf_dst.array(mfi);
1040  const Array4<Real const>& gpx_arr = gradp_temp[0].array(mfi);
1041  const Array4<Real const>& mf_mx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
1042  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1043  derdat(i ,j ,k, mf_comp) = myhalf * (gpx_arr(i+1,j,k) + gpx_arr(i,j,k)) * mf_mx_arr(i,j,0);
1044  });
1045  }
1046  mf_comp += 1;
1047  } // pres_hse_x
1048 
1049  if (containerHasElement(plot_var_names, "pres_hse_y"))
1050  {
1051  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1052  {
1053  const Box& bx = mfi.tilebox();
1054  const Array4<Real >& derdat = mf_dst.array(mfi);
1055  const Array4<Real const>& gpy_arr = gradp_temp[1].array(mfi);
1056  const Array4<Real const>& mf_my_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
1057  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1058  derdat(i ,j ,k, mf_comp) = myhalf * (gpy_arr(i,j+1,k) + gpy_arr(i,j,k)) * mf_my_arr(i,j,0);
1059  });
1060  }
1061  mf_comp += 1;
1062  } // pres_hse_y
1063 
1064  // **********************************************************************************************
1065  // Metric terms
1066  // **********************************************************************************************
1067 
1068  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
1069  if (containerHasElement(plot_var_names, "z_phys"))
1070  {
1071  MultiFab::Copy(mf_dst,*z_phys_cc[lev],0,mf_comp,1,0);
1072  mf_comp ++;
1073  }
1074 
1075  if (containerHasElement(plot_var_names, "detJ"))
1076  {
1077  MultiFab::Copy(mf_dst,*detJ_cc[lev],0,mf_comp,1,0);
1078  mf_comp ++;
1079  }
1080 
1081  //
1082  // Cell-centered averages of the terrain metric terms h_xi, h_eta and h_zeta
1083  //
1084  for (int imet(0); imet < 3; ++imet)
1085  {
1086  const std::string met_name = (imet == 0) ? "h_xi" : ((imet == 1) ? "h_eta" : "h_zeta");
1087 
1088  if (containerHasElement(plot_var_names, met_name))
1089  {
1090  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom[lev].InvCellSizeArray();
1091 #ifdef _OPENMP
1092 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1093 #endif
1094  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1095  {
1096  const Box& bx = mfi.tilebox();
1097  const Array4<Real >& derdat = mf_dst.array(mfi);
1098  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1099  if (imet == 0) {
1100  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1101  derdat(i,j,k,mf_comp) = Compute_h_xi_AtCellCenter (i,j,k,dxInv,z_nd);
1102  });
1103  } else if (imet == 1) {
1104  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1105  derdat(i,j,k,mf_comp) = Compute_h_eta_AtCellCenter (i,j,k,dxInv,z_nd);
1106  });
1107  } else {
1108  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1109  derdat(i,j,k,mf_comp) = Compute_h_zeta_AtCellCenter(i,j,k,dxInv,z_nd);
1110  });
1111  }
1112  }
1113  mf_comp ++;
1114  }
1115  } // h_xi, h_eta, h_zeta
1116  } // use_terrain
1117 
1118  if (containerHasElement(plot_var_names, "mapfac")) {
1119  amrex::Print() << "You are plotting a 3D version of mapfac; we suggest using the 2D plotfile instead" << std::endl;
1120 #ifdef _OPENMP
1121 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1122 #endif
1123  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1124  {
1125  const Box& bx = mfi.tilebox();
1126  const Array4<Real>& derdat = mf_dst.array(mfi);
1127  const Array4<Real>& mf_m = mapfac[lev][MapFacType::m_x]->array(mfi);
1128  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1129  derdat(i ,j ,k, mf_comp) = mf_m(i,j,0);
1130  });
1131  }
1132  mf_comp ++;
1133  }
1134 
1135  if (containerHasElement(plot_var_names, "lat_m")) {
1136  amrex::Print() << "You are plotting a 3D version of lat_m; we suggest using the 2D plotfile instead" << std::endl;
1137  if (lat_m[lev]) {
1138 #ifdef _OPENMP
1139 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1140 #endif
1141  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1142  {
1143  const Box& bx = mfi.tilebox();
1144  const Array4<Real>& derdat = mf_dst.array(mfi);
1145  const Array4<Real>& data = lat_m[lev]->array(mfi);
1146  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1147  derdat(i, j, k, mf_comp) = data(i,j,0);
1148  });
1149  }
1150  } else {
1151  mf_dst.setVal(0.0,mf_comp,1,0);
1152  }
1153  mf_comp++;
1154  } // lat_m
1155 
1156  if (containerHasElement(plot_var_names, "lon_m")) {
1157  amrex::Print() << "You are plotting a 3D version of lon_m; we suggest using the 2D plotfile instead" << std::endl;
1158  if (lon_m[lev]) {
1159 #ifdef _OPENMP
1160 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1161 #endif
1162  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1163  {
1164  const Box& bx = mfi.tilebox();
1165  const Array4<Real>& derdat = mf_dst.array(mfi);
1166  const Array4<Real>& data = lon_m[lev]->array(mfi);
1167  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1168  derdat(i, j, k, mf_comp) = data(i,j,0);
1169  });
1170  }
1171  } else {
1172  mf_dst.setVal(0.0,mf_comp,1,0);
1173  }
1174  mf_comp++;
1175  } // lon_m
1176 
1177  if (solverChoice.time_avg_vel) {
1179  vel_t_avg[lev] != nullptr,
1180  "time-average plot variables require vel_t_avg storage on every AMR level");
1181  const Real norm = static_cast<Real>(t_avg_cnt[lev]);
1182  auto copy_time_average = [&](const int source_comp) {
1183  if (norm > Real(0.0)) {
1184 #ifdef _OPENMP
1185 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1186 #endif
1187  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1188  {
1189  const Box& bx = mfi.tilebox();
1190  const Array4<Real>& derdat = mf_dst.array(mfi);
1191  const Array4<Real>& data = vel_t_avg[lev]->array(mfi);
1192  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1193  {
1194  derdat(i ,j ,k, mf_comp) = data(i,j,k,source_comp) / norm;
1195  });
1196  }
1197  } else {
1198  // A plot requested before the first accumulation is defined as zero.
1199  mf_dst.setVal(Real(0.0), mf_comp, 1, 0);
1200  }
1201  ++mf_comp;
1202  };
1203 
1204  if (containerHasElement(plot_var_names, "u_t_avg")) copy_time_average(0);
1205  if (containerHasElement(plot_var_names, "v_t_avg")) copy_time_average(1);
1206  if (containerHasElement(plot_var_names, "w_t_avg")) copy_time_average(2);
1207  if (containerHasElement(plot_var_names, "umag_t_avg")) copy_time_average(3);
1208  }
1209 
1212  interval_means[lev] != nullptr,
1213  "interval-mean plot variables require storage on every AMR level");
1214 
1215  const Real norm = static_cast<Real>(t_mean_cnt[lev]);
1216  const Real inv_norm = norm > Real(0.0) ? Real(1.0) / norm : Real(0.0);
1217 
1218  auto copy_interval_mean = [&](const int source_comp) {
1219  const int dest_comp = mf_comp++;
1220  if (norm <= Real(0.0)) {
1221  mf_dst.setVal(Real(0.0), dest_comp, 1, 0);
1222  return;
1223  }
1224 #ifdef _OPENMP
1225 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1226 #endif
1227  for (MFIter mfi(mf_dst, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1228  const Box& bx = mfi.tilebox();
1229  const Array4<Real>& out = mf_dst.array(mfi);
1230  const Array4<const Real>& moments = interval_means[lev]->const_array(mfi);
1231  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1232  out(i,j,k,dest_comp) = moments(i,j,k,source_comp) * inv_norm;
1233  });
1234  }
1235  };
1236 
1237  static constexpr const char* mean_names[] = {
1238  "u_mean", "v_mean", "w_mean", "theta_mean",
1239  "uu_mean", "vv_mean", "ww_mean", "uw_mean", "vw_mean", "wtheta_mean"
1240  };
1241  for (int source_comp = 0; source_comp < 10; ++source_comp) {
1242  if (containerHasElement(plot_var_names, mean_names[source_comp])) {
1243  copy_interval_mean(source_comp);
1244  }
1245  }
1246 
1247  auto copy_fluctuation = [&](const int product_comp,
1248  const int first_comp,
1249  const int second_comp) {
1250  const int dest_comp = mf_comp++;
1251  if (norm <= Real(0.0)) {
1252  mf_dst.setVal(Real(0.0), dest_comp, 1, 0);
1253  return;
1254  }
1255 #ifdef _OPENMP
1256 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1257 #endif
1258  for (MFIter mfi(mf_dst, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1259  const Box& bx = mfi.tilebox();
1260  const Array4<Real>& out = mf_dst.array(mfi);
1261  const Array4<const Real>& moments = interval_means[lev]->const_array(mfi);
1262  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1263  const Real product_mean = moments(i,j,k,product_comp) * inv_norm;
1264  const Real first_mean = moments(i,j,k,first_comp) * inv_norm;
1265  const Real second_mean = moments(i,j,k,second_comp) * inv_norm;
1266  out(i,j,k,dest_comp) = product_mean - first_mean * second_mean;
1267  });
1268  }
1269  };
1270 
1271  if (containerHasElement(plot_var_names, "uu_fluct")) copy_fluctuation(4, 0, 0);
1272  if (containerHasElement(plot_var_names, "vv_fluct")) copy_fluctuation(5, 1, 1);
1273  if (containerHasElement(plot_var_names, "ww_fluct")) copy_fluctuation(6, 2, 2);
1274  if (containerHasElement(plot_var_names, "uw_fluct")) copy_fluctuation(7, 0, 2);
1275  if (containerHasElement(plot_var_names, "vw_fluct")) copy_fluctuation(8, 1, 2);
1276  if (containerHasElement(plot_var_names, "wtheta_fluct")) copy_fluctuation(9, 2, 3);
1277 
1278  if (containerHasElement(plot_var_names, "tke_resolved")) {
1279  const int dest_comp = mf_comp++;
1280  if (norm <= Real(0.0)) {
1281  mf_dst.setVal(Real(0.0), dest_comp, 1, 0);
1282  } else {
1283 #ifdef _OPENMP
1284 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1285 #endif
1286  for (MFIter mfi(mf_dst, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1287  const Box& bx = mfi.tilebox();
1288  const Array4<Real>& out = mf_dst.array(mfi);
1289  const Array4<const Real>& moments = interval_means[lev]->const_array(mfi);
1290  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1291  const Real u_mean = moments(i,j,k,0) * inv_norm;
1292  const Real v_mean = moments(i,j,k,1) * inv_norm;
1293  const Real w_mean = moments(i,j,k,2) * inv_norm;
1294  const Real uu_fluct = moments(i,j,k,4) * inv_norm - u_mean * u_mean;
1295  const Real vv_fluct = moments(i,j,k,5) * inv_norm - v_mean * v_mean;
1296  const Real ww_fluct = moments(i,j,k,6) * inv_norm - w_mean * w_mean;
1297  out(i,j,k,dest_comp) = Real(0.5) * (uu_fluct + vv_fluct + ww_fluct);
1298  });
1299  }
1300  }
1301  }
1302  }
1303 
1304  const MultiFab* eta_src = nullptr;
1305  const bool have_native_shoc_diagnostics =
1306  solverChoice.turbChoice[lev].uses_native_shoc() &&
1307  native_shoc_driver[lev] &&
1308  native_shoc_driver[lev]->has_native_diagnostics();
1309  if (solverChoice.turbChoice[lev].use_kturb) {
1310  if (have_native_shoc_diagnostics) {
1311  eta_src = &native_shoc_driver[lev]->native_diagnostics();
1312  } else
1313  {
1314  eta_src = eddyDiffs_lev[lev].get();
1315  }
1317  eta_src != nullptr,
1318  "eddy-diffusivity plot variables require an eddy diagnostic source");
1319  }
1320 
1321  if (containerHasElement(plot_var_names, "nut")) {
1322  MultiFab dmf(mf_dst, make_alias, mf_comp, 1);
1323  MultiFab cmf(vars_new[lev][Vars::cons], make_alias, 0, 1); // to provide rho only
1324 #ifdef _OPENMP
1325 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1326 #endif
1327  for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1328  {
1329  const Box& bx = mfi.tilebox();
1330  auto prim = dmf[mfi].array();
1331  auto const cons = cmf[mfi].const_array();
1332  auto const diff = (eta_src) ? eta_src->const_array(mfi) :
1333  Array4<const Real>{};
1334  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1335  {
1336  const Real rho = cons(i, j, k, Rho_comp);
1337  const Real Kmv = diff(i, j, k, EddyDiff::Mom_v);
1338  prim(i,j,k) = Kmv / rho;
1339  });
1340  }
1341 
1342  mf_comp++;
1343  }
1344 
1345  const MultiFab* shoc_or_host_eddy = have_native_shoc_diagnostics
1346  ? &native_shoc_driver[lev]->native_diagnostics()
1347  : eddyDiffs_lev[lev].get();
1348 
1349  if (containerHasElement(plot_var_names, "Kmv") ||
1350  containerHasElement(plot_var_names, "Kmh") ||
1351  containerHasElement(plot_var_names, "Khv") ||
1352  containerHasElement(plot_var_names, "Khh") ||
1353  containerHasElement(plot_var_names, "Lturb")) {
1355  shoc_or_host_eddy != nullptr,
1356  "eddy diagnostic plot variables require an eddy diagnostic source");
1357  if (containerHasElement(plot_var_names, "Kmh") ||
1358  containerHasElement(plot_var_names, "Khh")) {
1360  eddyDiffs_lev[lev] != nullptr,
1361  "horizontal eddy diagnostic plot variables require eddyDiffs storage");
1362  }
1363  if (!have_native_shoc_diagnostics) {
1364  AMREX_ALWAYS_ASSERT(eddyDiffs_lev[lev] != nullptr);
1365  }
1366  }
1367 
1368  if (containerHasElement(plot_var_names, "Kmv")) {
1369  MultiFab::Copy(mf_dst,*shoc_or_host_eddy,EddyDiff::Mom_v,mf_comp,1,0);
1370  mf_comp ++;
1371  }
1372  if (containerHasElement(plot_var_names, "Kmh")) {
1373  MultiFab::Copy(mf_dst,*eddyDiffs_lev[lev],EddyDiff::Mom_h,mf_comp,1,0);
1374  mf_comp ++;
1375  }
1376  if (containerHasElement(plot_var_names, "Khv")) {
1377  MultiFab::Copy(mf_dst,*shoc_or_host_eddy,EddyDiff::Theta_v,mf_comp,1,0);
1378  mf_comp ++;
1379  }
1380  if (containerHasElement(plot_var_names, "Khh")) {
1381  MultiFab::Copy(mf_dst,*eddyDiffs_lev[lev],EddyDiff::Theta_h,mf_comp,1,0);
1382  mf_comp ++;
1383  }
1384  if (containerHasElement(plot_var_names, "Lturb")) {
1385  MultiFab::Copy(mf_dst,*shoc_or_host_eddy,EddyDiff::Turb_lengthscale,mf_comp,1,0);
1386  mf_comp ++;
1387  }
1388  // k-eqn RANS diagnostics (zero unless the closure is running)
1389  if (containerHasElement(plot_var_names, "Rt")) {
1390  AMREX_ALWAYS_ASSERT(eddyDiffs_lev[lev] != nullptr);
1391  MultiFab::Copy(mf_dst,*eddyDiffs_lev[lev],EddyDiff::RANS_Rt,mf_comp,1,0);
1392  mf_comp ++;
1393  }
1394  if (containerHasElement(plot_var_names, "cmu")) {
1395  AMREX_ALWAYS_ASSERT(eddyDiffs_lev[lev] != nullptr);
1396  MultiFab::Copy(mf_dst,*eddyDiffs_lev[lev],EddyDiff::RANS_cmu,mf_comp,1,0);
1397  mf_comp ++;
1398  }
1399  if (containerHasElement(plot_var_names, "cmu_prime")) {
1400  AMREX_ALWAYS_ASSERT(eddyDiffs_lev[lev] != nullptr);
1401  MultiFab::Copy(mf_dst,*eddyDiffs_lev[lev],EddyDiff::RANS_cmu_prime,mf_comp,1,0);
1402  mf_comp ++;
1403  }
1404  auto copy_native_shoc_diagnostic = [&](const MultiFab* src) {
1405  if (src != nullptr) {
1406  MultiFab::Copy(mf_dst, *src, 0, mf_comp, 1, 0);
1407  } else {
1408  mf_dst.setVal(-999, mf_comp, 1, 0);
1409  }
1410  mf_comp ++;
1411  };
1412  // Native SHOC pblh is diagnosed in meters AGL and is copied through
1413  // unchanged into the plotfile diagnostic field.
1414  if (containerHasElement(plot_var_names, "pblh")) {
1415  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1416  ? &native_shoc_driver[lev]->pblh_diagnostics()
1417  : nullptr);
1418  }
1419  if (containerHasElement(plot_var_names, "shoc_cldfrac")) {
1420  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1421  ? &native_shoc_driver[lev]->shoc_cldfrac_diagnostics()
1422  : nullptr);
1423  }
1424  if (containerHasElement(plot_var_names, "shoc_ql")) {
1425  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1426  ? &native_shoc_driver[lev]->shoc_ql_diagnostics()
1427  : nullptr);
1428  }
1429  if (containerHasElement(plot_var_names, "shoc_ql2")) {
1430  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1431  ? &native_shoc_driver[lev]->shoc_ql2_diagnostics()
1432  : nullptr);
1433  }
1434  if (containerHasElement(plot_var_names, "shoc_cond")) {
1435  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1436  ? &native_shoc_driver[lev]->shoc_cond_diagnostics()
1437  : nullptr);
1438  }
1439  if (containerHasElement(plot_var_names, "wqls_sec")) {
1440  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1441  ? &native_shoc_driver[lev]->wqls_sec_diagnostics()
1442  : nullptr);
1443  }
1444  if (containerHasElement(plot_var_names, "wthv_sec")) {
1445  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1446  ? &native_shoc_driver[lev]->wthv_sec_diagnostics()
1447  : nullptr);
1448  }
1449  if (containerHasElement(plot_var_names, "w_sec")) {
1450  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1451  ? &native_shoc_driver[lev]->w_sec_diagnostics()
1452  : nullptr);
1453  }
1454  if (containerHasElement(plot_var_names, "thl_sec")) {
1455  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1456  ? &native_shoc_driver[lev]->thl_sec_diagnostics()
1457  : nullptr);
1458  }
1459  if (containerHasElement(plot_var_names, "qw_sec")) {
1460  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1461  ? &native_shoc_driver[lev]->qw_sec_diagnostics()
1462  : nullptr);
1463  }
1464  if (containerHasElement(plot_var_names, "qwthl_sec")) {
1465  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1466  ? &native_shoc_driver[lev]->qwthl_sec_diagnostics()
1467  : nullptr);
1468  }
1469  if (containerHasElement(plot_var_names, "wthl_sec")) {
1470  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1471  ? &native_shoc_driver[lev]->wthl_sec_diagnostics()
1472  : nullptr);
1473  }
1474  if (containerHasElement(plot_var_names, "wqw_sec")) {
1475  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1476  ? &native_shoc_driver[lev]->wqw_sec_diagnostics()
1477  : nullptr);
1478  }
1479  if (containerHasElement(plot_var_names, "w3")) {
1480  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1481  ? &native_shoc_driver[lev]->w3_diagnostics()
1482  : nullptr);
1483  }
1484  if (containerHasElement(plot_var_names, "brunt")) {
1485  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1486  ? &native_shoc_driver[lev]->brunt_diagnostics()
1487  : nullptr);
1488  }
1489  if (containerHasElement(plot_var_names, "isotropy")) {
1490  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1491  ? &native_shoc_driver[lev]->isotropy_diagnostics()
1492  : nullptr);
1493  }
1494  if (containerHasElement(plot_var_names, "shear_prod")) {
1495  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1496  ? &native_shoc_driver[lev]->shear_prod_diagnostics()
1497  : nullptr);
1498  }
1499  if (containerHasElement(plot_var_names, "buoy_prod")) {
1500  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1501  ? &native_shoc_driver[lev]->buoy_prod_diagnostics()
1502  : nullptr);
1503  }
1504  if (containerHasElement(plot_var_names, "diss_tke")) {
1505  copy_native_shoc_diagnostic(have_native_shoc_diagnostics
1506  ? &native_shoc_driver[lev]->diss_tke_diagnostics()
1507  : nullptr);
1508  }
1509  if (containerHasElement(plot_var_names, "walldist")) {
1511  walldist[lev] != nullptr,
1512  "walldist plot variable requires wall-distance storage");
1513  MultiFab::Copy(mf_dst,*walldist[lev],0,mf_comp,1,0);
1514  mf_comp ++;
1515  }
1516  if (containerHasElement(plot_var_names, "diss")) {
1518  SFS_diss_lev[lev] != nullptr,
1519  "diss plot variable requires dissipation storage");
1520  MultiFab::Copy(mf_dst,*SFS_diss_lev[lev],0,mf_comp,1,0);
1521  mf_comp ++;
1522  }
1523 
1524  // ***************************************************************************************
1525  // Moisture variables.
1526  //
1527  // Which of these exist -- and where each one's data lives -- comes entirely from
1528  // solverChoice.moisture_indices (see the MoistureComponentIndices class comment).
1529  // Nothing below tests moisture_type, and nothing below is gated on the allocated
1530  // state width, so a component that a scheme allocates but never integrates is
1531  // never published as data.
1532  //
1533  // NOTE: the order here **MUST MATCH** the moisture entries of "derived_names" in
1534  // ERF.H, which is what supplies the plotfile header names. When the two
1535  // disagree, every name is paired with another field's data.
1536  // ***************************************************************************************
1537  if (mi.has_moisture()) {
1538 
1539  // Moist density: dry density plus vapor and the suspended condensate
1540  if (containerHasElement(plot_var_names, "moist_density"))
1541  {
1542  const auto list = mi.nonprecipitating_comps();
1543  assert_comps_in_state("moist_density", list);
1544  MultiFab::Copy(mf_dst, vars_new[lev][Vars::cons], Rho_comp, mf_comp, 1, 0);
1545  for (int n = 0; n < list.size; ++n) {
1546  MultiFab::Add(mf_dst, vars_new[lev][Vars::cons], list.comp[n], mf_comp, 1, 0);
1547  }
1548  mf_comp += 1;
1549  }
1550 
1551  // Mass mixing ratios of the individual species
1552  //--------------------------------------------------------------------------
1553  copy_moist_species("qv");
1554  copy_moist_species("qc");
1555  copy_moist_species("qi");
1556  copy_moist_species("qrain");
1557  copy_moist_species("qsnow");
1558  copy_moist_species("qgraup");
1559 
1560  // Total water: every mass species the scheme carries
1561  //--------------------------------------------------------------------------
1562  if (containerHasElement(plot_var_names, "qt"))
1563  {
1564  const auto list = mi.total_water_comps();
1565  assert_comps_in_state("qt", list);
1566  copy_moist_sum(list);
1567  }
1568 
1569  // Non-precipitating water: vapor plus the suspended condensate
1570  //--------------------------------------------------------------------------
1571  if (containerHasElement(plot_var_names, "qn"))
1572  {
1573  const auto list = mi.nonprecipitating_comps();
1574  assert_comps_in_state("qn", list);
1575  copy_moist_sum(list);
1576  }
1577 
1578  // Precipitating water: the falling species
1579  //--------------------------------------------------------------------------
1580  if (containerHasElement(plot_var_names, "qp"))
1581  {
1582  const auto list = mi.precipitating_comps();
1583  assert_comps_in_state("qp", list);
1584  copy_moist_sum(list);
1585  }
1586 
1587  if (containerHasElement(plot_var_names, "qsat"))
1588  {
1589  const bool anelastic = solverChoice.anelastic[lev];
1590  const Real rdOcp = solverChoice.rdOcp;
1591  const int qv_comp = mi.qv;
1592 #ifdef _OPENMP
1593 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1594 #endif
1595  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1596  {
1597  const Box& bx = mfi.tilebox();
1598  const Array4<Real>& derdat = mf_dst.array(mfi);
1599  const Array4<Real const>& p_arr = pressure.array(mfi);
1600  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
1601  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1602  {
1603  Real qv = S_arr(i,j,k,qv_comp) / S_arr(i,j,k,Rho_comp);
1604  Real T = anelastic ?
1605  getTgivenPandTh(p_arr(i,j,k), S_arr(i,j,k,RhoTheta_comp) /
1606  S_arr(i,j,k,Rho_comp), rdOcp) :
1607  getTgivenRandRTh(S_arr(i,j,k,Rho_comp), S_arr(i,j,k,RhoTheta_comp), qv);
1608  Real p = p_arr(i,j,k) * Real(0.01);
1609  erf_qsatw(T, p, derdat(i,j,k,mf_comp));
1610  });
1611  }
1612  mf_comp ++;
1613  }
1614 
1615  // Number concentrations
1616  //--------------------------------------------------------------------------
1617  // Note that nn, the CCN / total aerosol reservoir, may share a conserved
1618  // component with the cloud ice number of another scheme; the index map keeps
1619  // the two apart, so nothing here needs to know which scheme is running.
1620  copy_moist_species("nc");
1621  copy_moist_species("ni");
1622  copy_moist_species("nr");
1623  copy_moist_species("ns");
1624  copy_moist_species("ng");
1625  copy_moist_species("nn");
1626 
1627  // Surface accumulations, from the scheme's qmoist arrays
1628  //--------------------------------------------------------------------------
1629  copy_moist_diagnostic("rain_accum");
1630  copy_moist_diagnostic("snow_accum");
1631  copy_moist_diagnostic("graup_accum");
1632 
1633  // Relative humidity. A scheme either publishes it in a qmoist array or, as
1634  // SatAdj does, publishes no qmoist arrays at all and leaves it to be
1635  // recovered here from the conserved state.
1637  {
1638  if (containerHasElement(plot_var_names, "rel_humidity"))
1639  {
1640  const Real rdOcp = solverChoice.rdOcp;
1641  const bool anelastic = solverChoice.anelastic[lev];
1642  const int qv_comp = mi.qv;
1643  for (MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1644  const Box& bx = mfi.tilebox();
1645  const auto derdat = mf_dst.array(mfi);
1646  const auto state = vars_new[lev][Vars::cons].const_array(mfi);
1647  const auto p0 = p_hse.const_array(mfi);
1648  const auto pfield = pressure.const_array(mfi);
1649  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1650  const Real rho = state(i,j,k,Rho_comp);
1651  const Real qv = state(i,j,k,qv_comp) / rho;
1652  const Real theta = state(i,j,k,RhoTheta_comp) / rho;
1653  const Real p = anelastic ? p0(i,j,k) : pfield(i,j,k);
1654  const Real T = anelastic ?
1655  getTgivenPandTh(p0(i,j,k), theta, rdOcp) :
1656  getTgivenRandRTh(rho, state(i,j,k,RhoTheta_comp), qv);
1657  const Real vapor_pressure = p * qv / (RdoRv + qv);
1658  derdat(i,j,k,mf_comp) = vapor_pressure /
1659  (Real(100.0) * erf_esatw(T));
1660  });
1661  }
1662  ++mf_comp;
1663  }
1664  } else {
1665  copy_moist_diagnostic("rel_humidity");
1666  }
1667 
1668  copy_moist_diagnostic("condensation_rate");
1669 
1670  } // if has_moisture
1671 
1672  if (containerHasElement(plot_var_names, "terrain_IB_mask"))
1673  {
1674  MultiFab* terrain_blank = terrain_blanking[lev].get();
1675  MultiFab::Copy(mf_dst,*terrain_blank,0,mf_comp,1,0);
1676  mf_comp ++;
1677  }
1678 
1679  // Immersed-boundary surface energy balance: faces per cell and their mean skin temperature
1680  if (containerHasElement(plot_var_names, "ibseb_nfaces") ||
1681  containerHasElement(plot_var_names, "ibseb_tskin"))
1682  {
1683  MultiFab nfaces(grids[lev], dmap[lev], 1, 0);
1684  MultiFab tskin (grids[lev], dmap[lev], 1, 0);
1685  if (ibseb_params.enable && lev < static_cast<int>(m_ibseb.size()) && m_ibseb[lev]) {
1686  m_ibseb[lev]->scatter_diagnostics(nfaces, tskin);
1687  } else {
1688  nfaces.setVal(0.0);
1689  tskin.setVal(0.0);
1690  }
1691  if (containerHasElement(plot_var_names, "ibseb_nfaces")) {
1692  MultiFab::Copy(mf_dst, nfaces, 0, mf_comp, 1, 0);
1693  mf_comp++;
1694  }
1695  if (containerHasElement(plot_var_names, "ibseb_tskin")) {
1696  MultiFab::Copy(mf_dst, tskin, 0, mf_comp, 1, 0);
1697  mf_comp++;
1698  }
1699  }
1700  // Radiation on the faces, per-cell means: absorbed shortwave, shadow
1701  // flag, net longwave, sky view fraction
1702  for (const char* nm : {"ibseb_sw_abs", "ibseb_shadow", "ibseb_lw_net", "ibseb_f_sky", "ibseb_H", "ibseb_G"}) {
1703  if (!containerHasElement(plot_var_names, nm)) { continue; }
1704  MultiFab tmp(grids[lev], dmap[lev], 1, 0);
1705  if (ibseb_params.enable && lev < static_cast<int>(m_ibseb.size()) && m_ibseb[lev]) {
1706  const std::string s(nm);
1707  const auto& v = (s == "ibseb_sw_abs") ? m_ibseb[lev]->d_SW_abs
1708  : (s == "ibseb_shadow") ? m_ibseb[lev]->d_shadow
1709  : (s == "ibseb_lw_net") ? m_ibseb[lev]->d_LW_net
1710  : (s == "ibseb_H") ? m_ibseb[lev]->d_H
1711  : (s == "ibseb_G") ? m_ibseb[lev]->d_G
1712  : m_ibseb[lev]->d_f_sky;
1713  m_ibseb[lev]->scatter_field(v, tmp);
1714  } else {
1715  tmp.setVal(0.0);
1716  }
1717  MultiFab::Copy(mf_dst, tmp, 0, mf_comp, 1, 0);
1718  mf_comp++;
1719  }
1720 
1721  if (containerHasElement(plot_var_names, "volfrac")) {
1722  if ( solverChoice.terrain_type == TerrainType::EB ||
1723  solverChoice.terrain_type == TerrainType::ImmersedForcing)
1724  {
1725  MultiFab::Copy(mf_dst, EBFactory(lev).getVolFrac(), 0, mf_comp, 1, 0);
1726  } else {
1727  mf_dst.setVal(1.0, mf_comp, 1, 0);
1728  }
1729  mf_comp += 1;
1730  }
1731 
1732 #ifdef ERF_COMPUTE_ERROR
1733  // Next, check for error in velocities and if desired, output them -- note we output none or all, not just some
1734  if (containerHasElement(plot_var_names, "xvel_err") ||
1735  containerHasElement(plot_var_names, "yvel_err") ||
1736  containerHasElement(plot_var_names, "zvel_err"))
1737  {
1738  //
1739  // Moving terrain ANALYTICAL
1740  //
1741  Real H = geom[lev].ProbHi()[2];
1742  Real Ampl = Real(0.16);
1743  Real wavelength = Real(100.);
1744  Real kp = two * PI / wavelength;
1745  Real g = CONST_GRAV;
1746  Real omega = std::sqrt(g * kp);
1747  Real omega_t = omega * t_new[lev];
1748 
1749  const auto dx = geom[lev].CellSizeArray();
1750 
1751 #ifdef _OPENMP
1752 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1753 #endif
1754  for (MFIter mfi(mf_dst, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1755  {
1756  const Box& bx = mfi.validbox();
1757  Box xbx(bx); xbx.surroundingNodes(0);
1758  const Array4<Real> xvel_arr = vars_new[lev][Vars::xvel].array(mfi);
1759  const Array4<Real> zvel_arr = vars_new[lev][Vars::zvel].array(mfi);
1760 
1761  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1762 
1763  ParallelFor(xbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1764  {
1765  Real x = i * dx[0];
1766  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));
1767 
1768  Real z_base = Ampl * std::sin(kp * x - omega_t);
1769  z -= z_base;
1770 
1771  Real fac = std::cosh( kp * (z - H) ) / std::sinh(kp * H);
1772 
1773  xvel_arr(i,j,k) -= -Ampl * omega * fac * std::sin(kp * x - omega_t);
1774  });
1775 
1776  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1777  {
1778  Real x = (i + myhalf) * dx[0];
1779  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));
1780 
1781  Real z_base = Ampl * std::sin(kp * x - omega_t);
1782  z -= z_base;
1783 
1784  Real fac = std::sinh( kp * (z - H) ) / std::sinh(kp * H);
1785 
1786  zvel_arr(i,j,k) -= Ampl * omega * fac * std::cos(kp * x - omega_t);
1787  });
1788  }
1789 
1790  MultiFab temp_mf(mf_dst.boxArray(), mf_dst.DistributionMap(), AMREX_SPACEDIM, 0);
1791  average_face_to_cellcenter(temp_mf,0,
1792  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],&vars_new[lev][Vars::yvel],&vars_new[lev][Vars::zvel]});
1793 
1794  if (containerHasElement(plot_var_names, "xvel_err")) {
1795  MultiFab::Copy(mf_dst,temp_mf,0,mf_comp,1,0);
1796  mf_comp += 1;
1797  }
1798  if (containerHasElement(plot_var_names, "yvel_err")) {
1799  MultiFab::Copy(mf_dst,temp_mf,1,mf_comp,1,0);
1800  mf_comp += 1;
1801  }
1802  if (containerHasElement(plot_var_names, "zvel_err")) {
1803  MultiFab::Copy(mf_dst,temp_mf,2,mf_comp,1,0);
1804  mf_comp += 1;
1805  }
1806 
1807  // Now restore the velocities to what they were
1808 #ifdef _OPENMP
1809 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1810 #endif
1811  for (MFIter mfi(mf_dst, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1812  {
1813  const Box& bx = mfi.validbox();
1814  Box xbx(bx); xbx.surroundingNodes(0);
1815 
1816  const Array4<Real> xvel_arr = vars_new[lev][Vars::xvel].array(mfi);
1817  const Array4<Real> zvel_arr = vars_new[lev][Vars::zvel].array(mfi);
1818 
1819  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1820 
1821  ParallelFor(xbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1822  {
1823  Real x = i * dx[0];
1824  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));
1825  Real z_base = Ampl * std::sin(kp * x - omega_t);
1826 
1827  z -= z_base;
1828 
1829  Real fac = std::cosh( kp * (z - H) ) / std::sinh(kp * H);
1830  xvel_arr(i,j,k) += -Ampl * omega * fac * std::sin(kp * x - omega_t);
1831  });
1832  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1833  {
1834  Real x = (i + myhalf) * dx[0];
1835  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));
1836  Real z_base = Ampl * std::sin(kp * x - omega_t);
1837 
1838  z -= z_base;
1839  Real fac = std::sinh( kp * (z - H) ) / std::sinh(kp * H);
1840 
1841  zvel_arr(i,j,k) += Ampl * omega * fac * std::cos(kp * x - omega_t);
1842  });
1843  }
1844  } // end xvel_err, yvel_err, zvel_err
1845 
1846  if (containerHasElement(plot_var_names, "pp_err"))
1847  {
1848  // Moving terrain ANALYTICAL
1849 #ifdef _OPENMP
1850 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
1851 #endif
1852  for ( MFIter mfi(mf_dst,TilingIfNotGPU()); mfi.isValid(); ++mfi)
1853  {
1854  const Box& bx = mfi.tilebox();
1855  const Array4<Real>& derdat = mf_dst.array(mfi);
1856  const Array4<Real const>& p0_arr = p_hse.const_array(mfi);
1857  const Array4<Real const>& S_arr = vars_new[lev][Vars::cons].const_array(mfi);
1858 
1859  const auto dx = geom[lev].CellSizeArray();
1860  const Array4<Real const>& z_nd = z_phys_nd[lev]->const_array(mfi);
1861  const Array4<Real const>& p_arr = pressure.const_array(mfi);
1862  const Array4<Real const>& r0_arr = r_hse.const_array(mfi);
1863 
1864  Real H = geom[lev].ProbHi()[2];
1865  Real Ampl = Real(0.16);
1866  Real wavelength = Real(100.);
1867  Real kp = two * PI / wavelength;
1868  Real g = CONST_GRAV;
1869  Real omega = std::sqrt(g * kp);
1870  Real omega_t = omega * t_new[lev];
1871 
1872  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1873  {
1874  derdat(i, j, k, mf_comp) = p_arr(i,j,k) - p0_arr(i,j,k);
1875 
1876  Real rho_hse = r0_arr(i,j,k);
1877 
1878  Real x = (i + myhalf) * dx[0];
1879  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 )
1880  +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) );
1881  Real z_base = Ampl * std::sin(kp * x - omega_t);
1882 
1883  z -= z_base;
1884  Real fac = std::cosh( kp * (z - H) ) / std::sinh(kp * H);
1885  Real pprime_exact = -(Ampl * omega * omega / kp) * fac *
1886  std::sin(kp * x - omega_t) * r0_arr(i,j,k);
1887 
1888  derdat(i,j,k,mf_comp) -= pprime_exact;
1889  });
1890  }
1891  mf_comp += 1;
1892  }
1893 #endif
1894 
1895  if (solverChoice.rad_type != RadiationType::None) {
1896  if (containerHasElement(plot_var_names, "qsrc_sw") ||
1897  containerHasElement(plot_var_names, "qsrc_lw")) {
1899  qheating_rates[lev] != nullptr,
1900  "radiation source plot variables require qheating_rates storage");
1901  }
1902  if (containerHasElement(plot_var_names, "qsrc_sw")) {
1903  MultiFab::Copy(mf_dst, *(qheating_rates[lev]), 0, mf_comp, 1, 0);
1904  mf_comp += 1;
1905  }
1906  if (containerHasElement(plot_var_names, "qsrc_lw")) {
1907  MultiFab::Copy(mf_dst, *(qheating_rates[lev]), 1, mf_comp, 1, 0);
1908  mf_comp += 1;
1909  }
1910  }
1911 
1912  const bool plot_tau =
1913  containerHasElement(plot_var_names, "Tau11") ||
1914  containerHasElement(plot_var_names, "Tau12") ||
1915  containerHasElement(plot_var_names, "Tau13") ||
1916  containerHasElement(plot_var_names, "Tau21") ||
1917  containerHasElement(plot_var_names, "Tau22") ||
1918  containerHasElement(plot_var_names, "Tau23") ||
1919  containerHasElement(plot_var_names, "Tau31") ||
1920  containerHasElement(plot_var_names, "Tau32") ||
1921  containerHasElement(plot_var_names, "Tau33");
1922  if (plot_tau) {
1924  Tau[lev][TauType::tau11] != nullptr,
1925  "Tau plot variables require diffusion to be enabled");
1926  }
1927 
1928  if (containerHasElement(plot_var_names, "Tau11")) {
1929  MultiFab::Copy(mf_dst,*Tau[lev][TauType::tau11],0,mf_comp,1,0);
1930  mf_comp ++;
1931  }
1932 
1933  if (containerHasElement(plot_var_names, "Tau12")) {
1934  MultiFab::Copy(mf_dst,mf_cc_tau[lev],TauType::tau12,mf_comp,1,0);
1935  mf_comp ++;
1936  }
1937 
1938  if (containerHasElement(plot_var_names, "Tau13")) {
1939  MultiFab::Copy(mf_dst,mf_cc_tau[lev],TauType::tau13,mf_comp,1,0);
1940  mf_comp ++;
1941  }
1942 
1943  if (containerHasElement(plot_var_names, "Tau21")) {
1944  MultiFab::Copy(mf_dst,mf_cc_tau[lev],TauType::tau21,mf_comp,1,0);
1945  mf_comp ++;
1946  }
1947 
1948  if (containerHasElement(plot_var_names, "Tau22")) {
1949  MultiFab::Copy(mf_dst,*Tau[lev][TauType::tau22],0,mf_comp,1,0);
1950  mf_comp ++;
1951  }
1952 
1953  if (containerHasElement(plot_var_names, "Tau23")) {
1954  MultiFab::Copy(mf_dst,mf_cc_tau[lev],TauType::tau23,mf_comp,1,0);
1955  mf_comp ++;
1956  }
1957 
1958  if (containerHasElement(plot_var_names, "Tau31")) {
1959  MultiFab::Copy(mf_dst,mf_cc_tau[lev],TauType::tau31,mf_comp,1,0);
1960  mf_comp ++;
1961  }
1962 
1963  if (containerHasElement(plot_var_names, "Tau32")) {
1964  MultiFab::Copy(mf_dst,mf_cc_tau[lev],TauType::tau32,mf_comp,1,0);
1965  mf_comp ++;
1966  }
1967 
1968  if (containerHasElement(plot_var_names, "Tau33")) {
1969  MultiFab::Copy(mf_dst,*Tau[lev][TauType::tau33],0,mf_comp,1,0);
1970  mf_comp ++;
1971  }
1972 
1973  if (containerHasElement(plot_var_names, "hfx1")) {
1974  MultiFab::Copy(mf_dst,mf_cc_fx[lev],0,mf_comp,1,0);
1975  mf_comp ++;
1976  }
1977  if (containerHasElement(plot_var_names, "hfx2")) {
1978  MultiFab::Copy(mf_dst,mf_cc_fx[lev],1,mf_comp,1,0);
1979  mf_comp ++;
1980  }
1981  if (containerHasElement(plot_var_names, "hfx3")) {
1982  MultiFab::Copy(mf_dst,mf_cc_fx[lev],2,mf_comp,1,0);
1983  mf_comp ++;
1984  }
1985  if (containerHasElement(plot_var_names, "q1fx1")) {
1986  MultiFab::Copy(mf_dst,mf_cc_fx[lev],3,mf_comp,1,0);
1987  mf_comp ++;
1988  }
1989  if (containerHasElement(plot_var_names, "q1fx2")) {
1990  MultiFab::Copy(mf_dst,mf_cc_fx[lev],4,mf_comp,1,0);
1991  mf_comp ++;
1992  }
1993  if (containerHasElement(plot_var_names, "q1fx3")) {
1994  MultiFab::Copy(mf_dst,mf_cc_fx[lev],5,mf_comp,1,0);
1995  mf_comp ++;
1996  }
1997  if (containerHasElement(plot_var_names, "q2fx3")) {
1998  MultiFab::Copy(mf_dst,mf_cc_fx[lev],6,mf_comp,1,0);
1999  mf_comp ++;
2000  }
2001 
2002  // *****************************************************************************************
2003  // End of derived variables corresponding to "derived_names" in ERF.H
2004  //
2005  // Particles and microphysics can provide additional outputs, which are handled below.
2006  // *****************************************************************************************
2007 
2008 #ifdef ERF_USE_PARTICLES
2009  Vector<std::string> configured_particle_names;
2010  for (const auto& name : particleData.getNamesUnalloc()) {
2011  configured_particle_names.push_back(name);
2012  }
2013  for (const auto& name : particleData.getNames()) {
2014  if (!containerHasElement(configured_particle_names, name)) {
2015  configured_particle_names.push_back(name);
2016  }
2017  }
2018 
2019  const Vector<std::string> requested_particle_counts =
2021  configured_particle_names);
2022  for (const auto& name : requested_particle_counts) {
2023  MultiFab temp_dat(mf_dst.boxArray(), mf_dst.DistributionMap(), 1, 0);
2024  temp_dat.setVal(0);
2025  if (particleData.HasSpecies(name)) {
2026  particleData[name]->Increment(temp_dat, lev);
2027  }
2028  MultiFab::Copy(mf_dst, temp_dat, 0, mf_comp, 1, 0);
2029  mf_comp += 1;
2030  }
2031 
2032  Vector<std::string> particle_mesh_plot_names(0);
2033  particleData.GetMeshPlotVarNames( particle_mesh_plot_names );
2034 
2035  for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
2036  std::string plot_var_name(particle_mesh_plot_names[i]);
2037  if (containerHasElement(plot_var_names, plot_var_name) ) {
2038  MultiFab temp_dat(mf_dst.boxArray(), mf_dst.DistributionMap(), 1, 1);
2039  temp_dat.setVal(0);
2040  particleData.GetMeshPlotVar(plot_var_name, temp_dat, *z_phys_nd[lev], lev);
2041  MultiFab::Copy(mf_dst, temp_dat, 0, mf_comp, 1, 0);
2042  mf_comp += 1;
2043  }
2044  }
2045 #endif
2046 
2047  {
2048  Vector<std::string> microphysics_plot_names;
2049  micro->GetPlotVarNames(microphysics_plot_names);
2050  for (auto& plot_name : microphysics_plot_names) {
2051  if (containerHasElement(plot_var_names, plot_name)) {
2052  MultiFab temp_dat(mf_dst.boxArray(), mf_dst.DistributionMap(), 1, 1);
2053  temp_dat.setVal(0);
2054  micro->GetPlotVar(plot_name, temp_dat, lev);
2055  MultiFab::Copy(mf_dst, temp_dat, 0, mf_comp, 1, 0);
2056  mf_comp += 1;
2057  }
2058  }
2059  }
2060 
2062  mf_comp == ncomp_mf,
2063  "3D plotfile component assembly does not match selected variable names");
2064 }
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:36
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
constexpr amrex::Real Cp_l
Definition: ERF_Constants.H:38
constexpr amrex::Real R_d
Definition: ERF_Constants.H:34
constexpr amrex::Real L_v
Definition: ERF_Constants.H:51
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:44
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
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
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:396
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:264
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:159
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw_cc(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:98
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:39
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_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:215
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_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:240
void compute_divergence(int lev, amrex::MultiFab &rhs, amrex::Array< amrex::MultiFab const *, AMREX_SPACEDIM > rho0_u_const, amrex::MultiFab const &mf_mx, amrex::MultiFab const &mf_my, amrex::MultiFab const &mf_vx, amrex::MultiFab const &mf_uy, amrex::MultiFab const &ax_in, amrex::MultiFab const &ay_in, amrex::MultiFab const &dJ_in, amrex::Geometry const &geom_at_lev)
Definition: ERF_ComputeDivergence.cpp:18
amrex::EBFArrayBoxFactory const & EBFactory(int lev) const noexcept
Definition: ERF.H:2173
amrex::Vector< std::unique_ptr< IBFaceSet > > m_ibseb
Definition: ERF.H:1393
@ pi0_comp
Definition: ERF_IndexDefines.H:78
@ Turb_lengthscale
Definition: ERF_IndexDefines.H:254
@ Mom_h
Definition: ERF_IndexDefines.H:244
@ RANS_Rt
Definition: ERF_IndexDefines.H:266
@ RANS_cmu
Definition: ERF_IndexDefines.H:267
@ Theta_h
Definition: ERF_IndexDefines.H:245
@ RANS_cmu_prime
Definition: ERF_IndexDefines.H:268
@ P
Definition: ERF_IndexDefines.H:204
@ T
Definition: ERF_IndexDefines.H:128
void erf_dervortstretching(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:665
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:357
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:281
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:258
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:224
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:634
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:401
void erf_dernull(const Box &, FArrayBox &, int, int, const FArrayBox &, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:204
void erf_derprecipitable(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:759
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:549
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:372
void erf_dermucape(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:807
int plot3d_conserved_component_index(const std::string &name)
Conserved-state component behind a raw state variable name ("density", "rhotheta",...
Definition: ERF_PlotfileSelection.H:120
bool plot3d_needs_pressure(const amrex::Vector< std::string > &names)
Whether any requested variable needs the pressure field computed.
Definition: ERF_PlotfileSelection.H:263
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19
An ordered list of conserved-state components forming an aggregate moisture output variable (qt,...
Definition: ERF_DataStruct.H:463
The moisture data carried by the active microphysics scheme.
Definition: ERF_DataStruct.H:223
int comp_for_var(const std::string &name) const
The conserved-state component behind a single-species output name, or absent if the name is not a sin...
Definition: ERF_DataStruct.H:553
int rel_hum
relative humidity
Definition: ERF_DataStruct.H:266
static constexpr int computed_from_state
Definition: ERF_DataStruct.H:227
CompList nonprecipitating_comps() const
Components summed for non-precipitating water, "qn": vapor plus the suspended condensate....
Definition: ERF_DataStruct.H:491
CompList total_water_comps() const
Components summed for total water, "qt": every mass mixing ratio.
Definition: ERF_DataStruct.H:480
CompList precipitating_comps() const
Components summed for precipitating water, "qp": the falling species.
Definition: ERF_DataStruct.H:500
bool has_moisture() const
Test whether the active scheme carries any moisture at all.
Definition: ERF_DataStruct.H:541
int qmoist_index_for_var(const std::string &name) const
The qmoist slot behind a moist diagnostic output name.
Definition: ERF_DataStruct.H:578
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 
)

Read surface state data from a binary file and interpolate onto MultiFabs.

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

Locate the initial position of the hurricane eye.

Parameters
levcCurrent level.
mf_cc_velMultiFab containing cell-centered velocity.
velmag_thresholdVelocity magnitude threshold for eye identification.
[out]eye_xFound x-coordinate of the eye.
[out]eye_yFound y-coordinate of the eye.
Returns
True if the eye was found, false otherwise.
35 {
36  const auto dx = geom[levc].CellSizeArray();
37  const auto prob_lo = geom[levc].ProbLoArray();
38 
39  Gpu::DeviceVector<Real> d_coords(2, zero);
40  Gpu::DeviceVector<int> d_found(1,0);
41 
42  Real* d_coords_ptr = d_coords.data();
43  int* d_found_ptr = d_found.data();
44 
45  for (MFIter mfi(mf_cc_vel); mfi.isValid(); ++mfi)
46  {
47  const Box& box = mfi.validbox();
48  const Array4<const Real>& vel_arr = mf_cc_vel.const_array(mfi);
49 
50  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k)
51  {
52  Real magnitude = std::sqrt(vel_arr(i,j,k,0) * vel_arr(i,j,k,0) +
53  vel_arr(i,j,k,1) * vel_arr(i,j,k,1) +
54  vel_arr(i,j,k,2) * vel_arr(i,j,k,2));
55 
56  magnitude *= Real(3.6);
57 
58  Real z = prob_lo[2] + (k + myhalf) * dx[2];
59 
60  // Check if magnitude exceeds threshold
61  if (z < Real(2000.) && magnitude > velmag_threshold) {
62  // Use atomic operations to set found flag and store coordinates
63  Gpu::Atomic::Add(&d_found_ptr[0], 1); // Mark as found
64 
65  Real x = prob_lo[0] + (i + myhalf) * dx[0];
66  Real y = prob_lo[1] + (j + myhalf) * dx[1];
67 
68  // Store coordinates
69  Gpu::Atomic::Add(&d_coords_ptr[0],x); // Store x index
70  Gpu::Atomic::Add(&d_coords_ptr[1],y); // Store x index
71  }
72  });
73  }
74 
75  // Synchronize to ensure all threads complete their execution
76  amrex::Gpu::streamSynchronize(); // Wait for all GPU threads to finish
77 
78  Vector<int> h_found(1,0);
79  Gpu::copy(Gpu::deviceToHost, d_found.begin(), d_found.end(), h_found.begin());
80  ParallelAllReduce::Sum(h_found.data(), h_found.size(), ParallelContext::CommunicatorAll());
81 
82  // Broadcast coordinates if found
83  if (h_found[0] > 0) {
84  Vector<Real> h_coords(2,-bogus_large_value);
85  Gpu::copy(Gpu::deviceToHost, d_coords.begin(), d_coords.end(), h_coords.begin());
86 
87  ParallelAllReduce::Sum(h_coords.data(), h_coords.size(), ParallelContext::CommunicatorAll());
88 
89  eye_x = h_coords[0]/h_found[0];
90  eye_y = h_coords[1]/h_found[0];
91 
92  } else {
93  // Random large negative numbers so we don't trigger refinement in this case
94  eye_x = -bogus_large_value;
95  eye_y = -bogus_large_value;
96  }
97 
98  return (h_found[0] > 0);
99 }
Here is the call graph for this function:

◆ flush_stations()

void ERF::flush_stations ( ) const
1546 {
1547  if (station_sampler) { station_sampler->flush(); }
1548 }
std::unique_ptr< StationSampler > station_sampler
Definition: ERF.H:2127

◆ get_eb()

eb_ const& ERF::get_eb ( int  lev) const
inlineprivatenoexcept
2161  {
2162  AMREX_ASSERT(lev >= 0 && lev < eb.size() && eb[lev] != nullptr);
2163  return *eb[lev];
2164  }

◆ getAdvFluxReg()

AMREX_FORCE_INLINE amrex::YAFluxRegister* ERF::getAdvFluxReg ( int  lev)
inlineprivate
1938  {
1939  return advflux_reg[lev];
1940  }

◆ getCPUTime()

static double ERF::getCPUTime ( )
inlinestaticprivate
2030  {
2031  int numCores = amrex::ParallelDescriptor::NProcs();
2032 #ifdef _OPENMP
2033  numCores = numCores * omp_get_max_threads();
2034 #endif
2035 
2036  double T =
2037  numCores * (amrex::ParallelDescriptor::second() - startCPUTime) +
2039 
2040  return T;
2041  }
static double startCPUTime
Definition: ERF.H:2025
static double previousCPUTimeUsed
Definition: ERF.H:2026

◆ GetEnsembleCheckpointName()

void ERF::GetEnsembleCheckpointName ( int  da_iter,
int  ens_no 
)

◆ GetFillableAtmosToOceanLanes()

void ERF::GetFillableAtmosToOceanLanes ( bool  state_contract,
amrex::Vector< int > &  can_fill 
) const
302 {
303  // Each entry restates PackAtmosphericStates' own gate for that lane, which
304  // is why this lives beside it: changing a pack gate - re-enabling an #if 0
305  // block included - means changing the matching line here. Report false and
306  // the driver withholds the lane; report true wrongly and the ocean reads the
307  // driver's prefill as an atmospheric value.
308  //
309  // A configuration predicate, like HasRadiation()/HasCloudWater(): the lane
310  // will be written, not that any physics step has run.
311  const int lev = 0;
312  const bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
313  const bool has_radiation = HasRadiation();
314  const bool has_sfs_heat = (!SFS_hfx3_lev.empty() && SFS_hfx3_lev[lev] != nullptr);
315  const bool has_sfs_moist = (!SFS_q1fx3_lev.empty() && SFS_q1fx3_lev[lev] != nullptr);
316 
317  if (state_contract) {
318  can_fill.resize(nStateLanes, 0);
319  can_fill[iUwind] = 1; // always packed from Vars::xvel
320  can_fill[iVwind] = 1; // always packed from Vars::yvel
321  can_fill[iPatm] = 1; // always packed via getPgivenRTh
322  can_fill[iTair] = 1; // always packed via getTgivenRandRTh
323  // #if 0 below, pending Qair (RH vs specific humidity) and rain-rate
324  // semantics.
325  can_fill[iRH] = 0;
326  can_fill[iRain] = 0;
327  can_fill[iCloud] = (has_moisture &&
329  solverChoice.moisture_indices.qi != -1)) ? 1 : 0;
330  can_fill[iSWrad] = has_radiation ? 1 : 0;
331  can_fill[iLWrad] = has_radiation ? 1 : 0;
332  } else {
333  can_fill.resize(nFluxLanes, 0);
334  // tau_x/tau_y assert rather than skip, so gate on what they assert on.
335  can_fill[iTauX] = (Tau[lev][TauType::tau13] != nullptr) ? 1 : 0;
336  can_fill[iTauY] = (Tau[lev][TauType::tau23] != nullptr) ? 1 : 0;
337  can_fill[iSHflux] = has_sfs_heat ? 1 : 0;
338  can_fill[iLHflux] = has_sfs_moist ? 1 : 0;
339  can_fill[iFluxSWrad] = has_radiation ? 1 : 0;
340  can_fill[iFluxLWrad] = has_radiation ? 1 : 0;
341  can_fill[iFluxRain] = 0; // pack block is #if 0
342  can_fill[iFluxEvap] = has_sfs_moist ? 1 : 0;
343  }
344 }
bool HasRadiation() const
Definition: ERF.H:201
Here is the call graph for this function:

◆ GetLandMask()

void ERF::GetLandMask ( const amrex::iMultiFab *&  lmask) const
291 {
292  if (lmask_lev.empty() || lmask_lev[0].empty() || lmask_lev[0][0] == nullptr) {
293  lmask = nullptr;
294  return;
295  }
296  lmask = lmask_lev[0][0].get();
297 }

◆ GetOceanToAtmosCellCenterLonLat()

void ERF::GetOceanToAtmosCellCenterLonLat ( const amrex::MultiFab *&  lon_cc,
const amrex::MultiFab *&  lat_cc 
) const
278 {
279  if (lon_m.empty() || lat_m.empty() ||
280  lon_m[0] == nullptr || lat_m[0] == nullptr) {
281  lon_cc = nullptr;
282  lat_cc = nullptr;
283  return;
284  }
285  lon_cc = lon_m[0].get();
286  lat_cc = lat_m[0].get();
287 }

◆ GetOceanToAtmosSurfaceLayout()

void ERF::GetOceanToAtmosSurfaceLayout ( amrex::BoxArray &  ba,
amrex::DistributionMapping &  dm 
)
265 {
266  // The level-0 flattened cell layout.
267  // We take ba2d[0] directly and assert the shape the driver depends on.
269  !ba2d.empty() && ba2d[0].size() == grids[0].size(),
270  "ERF::GetOceanToAtmosSurfaceLayout requires the level-0 2D layout after InitData.");
271  ba = ba2d[0];
272  dm = dmap[0];
273 }

Referenced by ApplyOceanSurfaceState().

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

◆ GotoNextLine()

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

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

139 {
140  constexpr std::streamsize bl_ignore_max { 100000 };
141  is.ignore(bl_ignore_max, '\n');
142 }

◆ grids_are_split_in_z()

bool ERF::grids_are_split_in_z ( const amrex::BoxArray &  ba)
staticprivate

Is any (i,j) column of this BoxArray covered by more than one box?

Projecting every box onto a common z index turns "two boxes stacked in z" into "two overlapping 2D boxes", so a non-disjoint projection is exactly the test for a grid that has been decomposed in the vertical.

Parameters
[in]baBoxArray to test
614 {
615  BoxList bl(ba.ixType());
616  for (int i(0); i < ba.size(); ++i) {
617  Box b(ba[i]); b.setRange(2,0);
618  bl.push_back(b);
619  }
620  return !(BoxArray(std::move(bl)).isDisjoint());
621 }

◆ has_own_init_file()

bool ERF::has_own_init_file ( int  lev) const
1834 {
1835  if (lev < 0 || lev >= static_cast<int>(nc_init_file.size())) { return false; }
1836  return !nc_init_file[lev].empty();
1837 }

◆ has_surface_layer_inputs()

bool ERF::has_surface_layer_inputs ( const std::string &  prefix) const
private

Whether the user wrote any surface-layer input under the given prefix.

This asks the snapshot taken in ReadParameters, not the live ParmParse table. The live table cannot answer the question: ERF_InputSoundingData.H queries erf.most.surf_temp and erf.most.surf_moist with queryAdd while reading the sounding, which runs during InitData_pre and leaves both keys in the table holding their negative sentinels. Testing the live table would therefore report unqualified MOST inputs for every input_sounding run, whether or not the user wrote any.

Parameters
[in]prefixthe prefix to test, e.g. "erf" or "erf.zlo"
3829 {
3830  const std::string most_prefix = prefix + ".most.";
3831  const std::string surface_layer_prefix = prefix + ".surface_layer.";
3832 
3833  for (const auto& key : user_specified_inputs) {
3834  if (key.compare(0, most_prefix.size(), most_prefix) == 0 ||
3835  key.compare(0, surface_layer_prefix.size(), surface_layer_prefix) == 0) {
3836  return true;
3837  }
3838  }
3839  return false;
3840 }
std::set< std::string > user_specified_inputs
Definition: ERF.H:855

◆ HasCloudWater()

bool ERF::HasCloudWater ( ) const
inline
214  {
215  return (solverChoice.moisture_type != MoistureType::None) &&
218  }

◆ HasRadiation()

bool ERF::HasRadiation ( ) const
inline
201  {
202  return (!rad_fluxes.empty() && rad_fluxes[0] != nullptr);
203  }

Referenced by GetFillableAtmosToOceanLanes(), and PackAtmosphericStates().

Here is the caller graph for this function:

◆ HurricaneEyeTracker()

void ERF::HurricaneEyeTracker ( const SolverChoice sc)

Wrapper to track the hurricane eye position over time.

Parameters
[in]scSolver choices
641 {
642  static bool is_start = true;
643  int levc=finest_level;
644 
645  const MoistureType moisture_type = sc.moisture_type;
646  const Real hurricane_eye_latitude = sc.hurricane_eye_latitude;
647  const Real hurricane_eye_longitude = sc.hurricane_eye_longitude;
648 
649  if(is_start and restart_chkfile.empty()){
650  HurricaneEyeTrackerInitial(sc, geom[levc],
651  vars_new[levc],
652  hurricane_eye_latitude,
653  hurricane_eye_longitude);
654  is_start = false;
655  } else {
656  if(!restart_chkfile.empty()) {
658  }
659  HurricaneEyeTrackerNotInitial(sc, geom[levc], vars_new[levc],
660  moisture_type);
661  }
663 }
void HurricaneEyeTrackerNotInitial(const SolverChoice &sc, const amrex::Geometry &geom, const amrex::Vector< amrex::MultiFab > &S_data, MoistureType moisture_type)
Definition: ERF_HurricaneDiagnostics.cpp:368
void HurricaneEyeTrackerInitial(const SolverChoice &sc, 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.cpp:229
std::string restart_chkfile
Definition: ERF.H:1501
void ReadStormTrackerRestart()
Definition: ERF_HurricaneDiagnostics.cpp:475
void HurricaneTrackerCircle()
Definition: ERF_HurricaneDiagnostics.cpp:194
amrex::Real hurricane_eye_longitude
Initial hurricane-eye longitude.
Definition: ERF_DataStruct.H:2321
amrex::Real hurricane_eye_latitude
Initial hurricane-eye latitude.
Definition: ERF_DataStruct.H:2321

◆ HurricaneEyeTrackerInitial()

void ERF::HurricaneEyeTrackerInitial ( const SolverChoice sc,
const amrex::Geometry &  geom,
const amrex::Vector< amrex::MultiFab > &  S_data,
const amrex::Real hurricane_eye_latitude,
const amrex::Real hurricane_eye_longitude 
)

Initialize the hurricane eye tracker using a given latitude and longitude.

Parameters
[in]scSolver choices
[in]lev_geomGeometry of the current level
[in]S_dataConservative state data
[in]hurricane_eye_latitudeTarget latitude for the eye
[in]hurricane_eye_longitudeTarget longitude for the eye
234 {
235  int levc = finest_level;
236  Gpu::DeviceScalar<Real> d_val_min(1e10);
237  Gpu::DeviceScalar<int> d_i_min(-1), d_j_min(-1);
238 
239  Real* d_val_min_ptr = d_val_min.dataPtr();
240  int* d_i_min_ptr = d_i_min.dataPtr();
241  int* d_j_min_ptr = d_j_min.dataPtr();
242 
243  // NOTE: the arg-min is done in two passes. A single pass that takes an
244  // atomic min of the distance and then plainly stores i and j is
245  // racy: a thread with a worse distance can still win the store and
246  // leave an eye location that does not belong to the recorded
247  // minimum. The first pass reduces the distance only; the second
248  // records the location of the cells that attain it, through one
249  // atomic on a packed index so value and location stay consistent.
250  const Dim3 dlo = lbound(lev_geom.Domain());
251  const int nx = lev_geom.Domain().length(0);
252 
253  Gpu::DeviceScalar<Long> d_idx_min(std::numeric_limits<Long>::max());
254  Long* d_idx_min_ptr = d_idx_min.dataPtr();
255 
256  if(sc.init_type == InitType::WRFInput){
257  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
258  const Box& box = mfi.validbox();
259  FArrayBox& fab_lat = (*(lat_m[levc]))[mfi];
260  FArrayBox& fab_lon = (*(lon_m[levc]))[mfi];
261  const Array4<Real>& lat_arr = fab_lat.array();
262  const Array4<Real>& lon_arr = fab_lon.array();
263 
264  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
265  if (k==0) {
266 
267  Real dlat = lat_arr(i,j,0) - hurricane_eye_latitude;
268  Real dlon = lon_arr(i,j,0) - hurricane_eye_longitude;
269  Real dist = std::sqrt(dlat*dlat + dlon*dlon);
270  Gpu::Atomic::Min(&d_val_min_ptr[0], dist);
271  }
272  });
273  }
274 
275  // The minimum over every box on this rank must be known before the
276  // locating pass below can test against it.
277  Gpu::synchronize();
278 
279  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
280  const Box& box = mfi.validbox();
281  FArrayBox& fab_lat = (*(lat_m[levc]))[mfi];
282  FArrayBox& fab_lon = (*(lon_m[levc]))[mfi];
283  const Array4<Real>& lat_arr = fab_lat.array();
284  const Array4<Real>& lon_arr = fab_lon.array();
285 
286  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
287  if (k==0) {
288 
289  Real dlat = lat_arr(i,j,0) - hurricane_eye_latitude;
290  Real dlon = lon_arr(i,j,0) - hurricane_eye_longitude;
291  Real dist = std::sqrt(dlat*dlat + dlon*dlon);
292  if (dist == d_val_min_ptr[0]) {
293  Gpu::Atomic::Min(d_idx_min_ptr, pack_ij(i,j,nx,dlo));
294  }
295  }
296  });
297  }
298  }
299 
300  if(sc.init_type == InitType::HindCast){
301  for (amrex::MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
302  const amrex::Box& box = mfi.validbox();
303  const auto& mf_latlon = forecast_state_interp[levc][4];
304  const auto latlon_arr = mf_latlon.array(mfi);
305 
306  amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
307  if (k==0) {
308 
309  amrex::Real dlat = latlon_arr(i,j,k,0) - hurricane_eye_latitude;
310  amrex::Real dlon = latlon_arr(i,j,k,1) - hurricane_eye_longitude;
311  amrex::Real dist = std::sqrt(dlat*dlat + dlon*dlon);
312  amrex::Gpu::Atomic::Min(&d_val_min_ptr[0], dist);
313  }
314  });
315  }
316 
317  // The minimum over every box on this rank must be known before the
318  // locating pass below can test against it.
319  Gpu::synchronize();
320 
321  for (amrex::MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
322  const amrex::Box& box = mfi.validbox();
323  const auto& mf_latlon = forecast_state_interp[levc][4];
324  const auto latlon_arr = mf_latlon.array(mfi);
325 
326  amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
327  if (k==0) {
328 
329  amrex::Real dlat = latlon_arr(i,j,k,0) - hurricane_eye_latitude;
330  amrex::Real dlon = latlon_arr(i,j,k,1) - hurricane_eye_longitude;
331  amrex::Real dist = std::sqrt(dlat*dlat + dlon*dlon);
332  if (dist == d_val_min_ptr[0]) {
333  amrex::Gpu::Atomic::Min(d_idx_min_ptr, pack_ij(i,j,nx,dlo));
334  }
335  }
336  });
337  }
338  }
339 
340  Gpu::synchronize();
341 
342  // Unpack the located index back into the (i,j) device scalars that
343  // ComputeGlobalMinLocation reads.
344  {
345  int h_i_min, h_j_min;
346  unpack_ij(d_idx_min.dataValue(), nx, dlo, h_i_min, h_j_min);
347  Gpu::copy(Gpu::hostToDevice, &h_i_min, &h_i_min + 1, d_i_min_ptr);
348  Gpu::copy(Gpu::hostToDevice, &h_j_min, &h_j_min + 1, d_j_min_ptr);
349  }
350 
351  Real global_val_min;
352  int global_i_min, global_j_min;
353 
354  ComputeGlobalMinLocation(sc, lev_geom, S_data,
355  d_val_min_ptr, d_i_min_ptr, d_j_min_ptr,
356  global_val_min, global_i_min, global_j_min);
357 }
void ComputeGlobalMinLocation(const SolverChoice &sc, 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.cpp:77
Here is the call graph for this function:

◆ HurricaneEyeTrackerNotInitial()

void ERF::HurricaneEyeTrackerNotInitial ( const SolverChoice sc,
const amrex::Geometry &  geom,
const amrex::Vector< amrex::MultiFab > &  S_data,
MoistureType  moisture_type 
)

Track the hurricane eye by searching for minimum pressure near the previous position.

Parameters
[in]scSolver choices
[in]lev_geomGeometry of the current level
[in]S_dataConservative state data
[in]moisture_typeMoisture model type
372 {
373 
374  if (hurricane_eye_track_xy.empty()) {
375  Print() << "Error: hurricane_eye_track_xy is empty!\n";
376  Abort("Attempted to access hurricane_eye_track_xy[0]");
377  }
378 
379  Real tmp_x_eye = hurricane_eye_track_xy.back()[0];
380  Real tmp_y_eye = hurricane_eye_track_xy.back()[1];
381 
382  if(ParallelDescriptor::IOProcessor()){
383  std::cout << "The value of x y are " << tmp_x_eye << " " << tmp_y_eye << std::endl;
384  }
385 
386  Gpu::DeviceScalar<Real> d_val_min(1e10);
387  Gpu::DeviceScalar<int> d_i_min(-1), d_j_min(-1);
388 
389  Real* d_val_min_ptr = d_val_min.dataPtr();
390  int* d_i_min_ptr = d_i_min.dataPtr();
391  int* d_j_min_ptr = d_j_min.dataPtr();
392 
393  bool use_moisture = (moisture_type != MoistureType::None);
394  const int ncomp = S_data[IntVars::cons].nComp();
395 
396  const auto dx = lev_geom.CellSizeArray();
397  const auto prob_lo = lev_geom.ProbLoArray();
398 
399  // NOTE: see HurricaneEyeTrackerInitial -- the arg-min is split into a
400  // reducing pass and a locating pass so that the recorded (i,j)
401  // always belongs to the cell holding the recorded minimum.
402  const Dim3 dlo = lbound(lev_geom.Domain());
403  const int nx = lev_geom.Domain().length(0);
404 
405  Gpu::DeviceScalar<Long> d_idx_min(std::numeric_limits<Long>::max());
406  Long* d_idx_min_ptr = d_idx_min.dataPtr();
407 
408  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
409  const Box& box = mfi.validbox();
410  const Array4<Real const>& S_arr = S_data[IntVars::cons].const_array(mfi);
411 
412  ParallelFor(box,[=] AMREX_GPU_DEVICE(int i, int j, int k) {
413  if(k==0) {
414  Real x = prob_lo[0] + (i+myhalf)*dx[0];
415  Real y = prob_lo[1] + (j+myhalf)*dx[1];
416  Real dist = std::sqrt((x-tmp_x_eye)*(x-tmp_x_eye) + (y-tmp_y_eye)*(y-tmp_y_eye));
417  if(dist < 200e3) {
418  Real qv_for_p = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : 0;
419  const Real rhotheta = S_arr(i,j,k,RhoTheta_comp);
420  Real pressure = getPgivenRTh(rhotheta,qv_for_p);
421  Gpu::Atomic::Min(&d_val_min_ptr[0], pressure);
422  }
423  }
424  });
425  }
426 
427  // The minimum over every box on this rank must be known before the
428  // locating pass below can test against it.
429  Gpu::synchronize();
430 
431  for (MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi) {
432  const Box& box = mfi.validbox();
433  const Array4<Real const>& S_arr = S_data[IntVars::cons].const_array(mfi);
434 
435  ParallelFor(box,[=] AMREX_GPU_DEVICE(int i, int j, int k) {
436  if(k==0) {
437  Real x = prob_lo[0] + (i+myhalf)*dx[0];
438  Real y = prob_lo[1] + (j+myhalf)*dx[1];
439  Real dist = std::sqrt((x-tmp_x_eye)*(x-tmp_x_eye) + (y-tmp_y_eye)*(y-tmp_y_eye));
440  if(dist < 200e3) {
441  Real qv_for_p = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : 0;
442  const Real rhotheta = S_arr(i,j,k,RhoTheta_comp);
443  Real pressure = getPgivenRTh(rhotheta,qv_for_p);
444  if (pressure == d_val_min_ptr[0]) {
445  Gpu::Atomic::Min(d_idx_min_ptr, pack_ij(i,j,nx,dlo));
446  }
447  }
448  }
449  });
450  }
451 
452  Gpu::synchronize();
453 
454  // Unpack the located index back into the (i,j) device scalars that
455  // ComputeGlobalMinLocation reads.
456  {
457  int h_i_min, h_j_min;
458  unpack_ij(d_idx_min.dataValue(), nx, dlo, h_i_min, h_j_min);
459  Gpu::copy(Gpu::hostToDevice, &h_i_min, &h_i_min + 1, d_i_min_ptr);
460  Gpu::copy(Gpu::hostToDevice, &h_j_min, &h_j_min + 1, d_j_min_ptr);
461  }
462 
463  Real global_val_min;
464  int global_i_min, global_j_min;
465 
466  ComputeGlobalMinLocation(sc, lev_geom, S_data,
467  d_val_min_ptr, d_i_min_ptr, d_j_min_ptr,
468  global_val_min, global_i_min, global_j_min);
469 }
Here is the call graph for this function:

◆ HurricaneMaxVelTracker()

void ERF::HurricaneMaxVelTracker ( const amrex::Geometry &  geom,
const amrex::MultiFab &  mf_cc_vel,
const double &  time 
)

Compute and track the maximum wind velocity near the hurricane eye.

Parameters
[in]lev_geomGeometry of the current level
[in]mf_cc_velMultiFab containing cell-centered velocity
[in]timeCurrent simulation time
676 {
677  const int ncomp = AMREX_SPACEDIM;
678 
679  Real* d_val_max_ptr;
680  Gpu::DeviceVector<Real> d_val_max(1, -bogus_large_value);
681  d_val_max_ptr = d_val_max.data();
682 
683  const auto [x_last, y_last] = hurricane_eye_track_xy.back();
684  const auto dx = lev_geom.CellSizeArray();
685  const auto prob_lo = lev_geom.ProbLoArray();
686 
687  Real x_eye = x_last;
688  Real y_eye = y_last;
689 
690  for (MFIter mfi(mf_cc_vel); mfi.isValid(); ++mfi) {
691  const Box& box = mfi.validbox();
692  const auto& vel_arr = mf_cc_vel.const_array(mfi);
693 
694  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
695  Real x = prob_lo[0] + (i+myhalf)*dx[0];
696  Real y = prob_lo[1] + (j+myhalf)*dx[1];
697  Real dist = std::sqrt((x-x_eye)*(x-x_eye) +
698  (y-y_eye)*(y-y_eye));
699  if(k==1 && dist < 200e3) {
700  Real velmag = zero;
701  for (int comp = 0; comp < ncomp; ++comp) {
702  Real vel = vel_arr(i, j, k, comp);
703  velmag += vel * vel;
704  }
705  velmag = std::sqrt(velmag)*Real(3.6); // km/hr
706  Gpu::Atomic::Max(&d_val_max_ptr[0], velmag);
707  }
708  });
709  }
710 
711  Gpu::synchronize();
712 
713  Real h_val_max_local = -bogus_large_value;
714  Gpu::copy(Gpu::deviceToHost, d_val_max.begin(), d_val_max.end(), &h_val_max_local);
715 
716  Real h_val_max_global = -bogus_large_value;
717  #ifdef AMREX_USE_MPI
718  h_val_max_global = h_val_max_local;
719  amrex::ParallelDescriptor::ReduceRealMax(h_val_max_global);
720  #else
721  h_val_max_global = h_val_max_local;
722  #endif
723 
724  double time_in_hrs = time / 3600.0;
725  hurricane_maxvel_vs_time.push_back({static_cast<Real>(time_in_hrs), h_val_max_global});
726 }
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_maxvel_vs_time
Definition: ERF.H:290
Here is the call graph for this function:

◆ HurricaneMinPressureTracker()

void ERF::HurricaneMinPressureTracker ( MoistureType  moisture_type,
const amrex::Geometry &  geom,
const amrex::MultiFab &  mf_cons_var,
const double &  time 
)

Compute and track the minimum pressure near the hurricane eye.

Parameters
[in]moisture_typeMoisture model type
[in]lev_geomGeometry of the current level
[in]mf_cons_varMultiFab containing conservative variables
[in]timeCurrent simulation time
741 {
742 
743 
744  Real* d_val_min_ptr;
745  Gpu::DeviceVector<Real> d_val_min(1, bogus_large_value);
746  d_val_min_ptr = d_val_min.data();
747 
748  const Real x_last = hurricane_eye_track_xy.back()[0];
749  const Real y_last = hurricane_eye_track_xy.back()[1];
750  const auto dx = lev_geom.CellSizeArray();
751  const auto prob_lo = lev_geom.ProbLoArray();
752 
753  const int ncomp = mf_cons_var.nComp();
754  bool use_moisture = (moisture_type != MoistureType::None);
755 
756  for (MFIter mfi(mf_cons_var); mfi.isValid(); ++mfi) {
757  const Box& box = mfi.validbox();
758  const auto& S_arr = mf_cons_var.const_array(mfi);
759 
760  ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
761  Real x = prob_lo[0] + (i+myhalf)*dx[0];
762  Real y = prob_lo[1] + (j+myhalf)*dx[1];
763  Real dist2 = (x-x_last)*(x-x_last) +
764  (y-y_last)*(y-y_last);
765  if(k==1 && dist2 < 200e3*200e3) {
766  const Real rhotheta = S_arr(i,j,k,RhoTheta_comp);
767  const Real qv_for_p = (use_moisture && (ncomp > RhoQ1_comp)) ? S_arr(i,j,k,RhoQ1_comp)/S_arr(i,j,k,Rho_comp) : 0;
768  const Real pressure = getPgivenRTh(rhotheta,qv_for_p);
769  Gpu::Atomic::Min(&d_val_min_ptr[0], pressure);
770  }
771  });
772  }
773 
774  Gpu::synchronize();
775 
776  Real h_val_min_local = bogus_large_value;
777  Gpu::copy(Gpu::deviceToHost, d_val_min.begin(), d_val_min.end(), &h_val_min_local);
778 
779  // NOTE: use the amrex wrapper rather than a hard-coded MPI_DOUBLE, which is
780  // a type mismatch when amrex::Real is float (ERF_PRECISION=SINGLE).
781  Real h_val_min_global = h_val_min_local;
782  ParallelDescriptor::ReduceRealMin(h_val_min_global);
783 
784  double time_in_hrs = time / 3600.0;
785  hurricane_minpressure_vs_time.push_back({static_cast<Real>(time_in_hrs), h_val_min_global});
786 }
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_minpressure_vs_time
Definition: ERF.H:291
Here is the call graph for this function:

◆ HurricaneTracker()

void ERF::HurricaneTracker ( int  lev,
double  time,
const amrex::MultiFab &  cc_vel,
const amrex::Real  velmag_threshold,
amrex::TagBoxArray *  tags = nullptr 
)

Track the hurricane eye and tag cells for refinement.

Parameters
levcCurrent level.
timeCurrent simulation time.
mf_cc_velMultiFab containing cell-centered velocity.
velmag_thresholdVelocity magnitude threshold for eye identification.
[out]tagsTagBoxArray to be filled for refinement.
152 {
153  bool is_found;
154 
155  Real eye_x, eye_y;
156 
157  if (time==zero || hurricane_eye_track_xy.empty()) {
158  is_found = FindInitialEye(levc, mf_cc_vel, velmag_threshold, eye_x, eye_y);
159  } else {
160  is_found = true;
161  const auto& last = hurricane_eye_track_xy.back();
162  eye_x = last[0];
163  eye_y = last[1];
164  }
165 
166  if (is_found) {
167  const int exponent = max_level-1-levc;
168  Real rad_tag = std::ldexp(Real(4.e5), exponent);
169  tag_on_distance_from_eye(geom[levc], tags, eye_x, eye_y, rad_tag);
170  }
171 }
void tag_on_distance_from_eye(const Geometry &cgeom, TagBoxArray *tags, const Real eye_x, const Real eye_y, const Real rad_tag)
Tag cells based on 2D distance from the storm eye.
Definition: ERF_RefineHurricane.cpp:111
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:31
Here is the call graph for this function:

◆ HurricaneTrackerCircle()

void ERF::HurricaneTrackerCircle ( )

Generate a circular set of points around the last known eye position.

195 {
196  // Check that there is at least one eye position
197  if (hurricane_eye_track_xy.empty()) return;
198 
199  // Get the last known (x, y) position of the eye
200  const auto [x_last, y_last] = hurricane_eye_track_xy.back();
201 
202  // Define circle properties
203  const int n_points = 100; // number of points on the circle
204  const Real radius = 200e3; // radius in meters (example: 50 km)
205 
206  // Clear previous points and reserve space
207  hurricane_tracker_circle.clear();
208  hurricane_tracker_circle.reserve(n_points);
209 
210  // Fill the circle points
211  for (int i = 0; i < n_points; ++i) {
212  Real theta = two * static_cast<Real>(M_PI) * static_cast<Real>(i) / static_cast<Real>(n_points);
213  Real x = x_last + radius * std::cos(theta);
214  Real y = y_last + radius * std::sin(theta);
215  hurricane_tracker_circle.push_back({x, y});
216  }
217 }
#define M_PI
Definition: ERF_HurricaneDiagnostics.cpp:27
amrex::Vector< std::array< amrex::Real, 2 > > hurricane_tracker_circle
Definition: ERF.H:292

◆ ibseb_advance()

void ERF::ibseb_advance ( int  lev,
amrex::Real  time,
amrex::Real  dt_lev,
const amrex::MultiFab &  cons,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  zvel 
)

Per-step update of one level, called at the start of ERF::Advance() with the state at the start of the step: shortwave, longwave and the wall function on the faces, then either the prognostic balance (which finds the skin temperature at the end of the step and advances the slab with it) or, with erf.ibseb.prognostic = false, the slab alone under the fixed skin. The sensible flux left in the set is what add_heat_flux_to_source() deposits at every slow stage of the step, so the air receives exactly the H of the closed balance. The atmosphere is seen at the start of the step and the skin is implicit within it, the usual coupling of a land-surface model.

Parameters
[in]levAMR level to update; a no-op if it has no face set.
[in]timeTime at the start of the step [s], at which the sun position of the shortwave is taken.
[in]dt_levLength of the level's step [s], over which the slab is advanced and within which the skin is implicit.
[in]consConserved state at the start of the step: the air temperature of the longwave and the wall function, and the profile of the bulk Richardson depth.
[in]xvelFace-centred x velocity at the start of the step.
[in]yvelFace-centred y velocity at the start of the step.
[in]zvelFace-centred z velocity at the start of the step; the three together drive the wall function of the sensible flux.
147 {
148  if (!ibseb_params.enable || lev >= static_cast<int>(m_ibseb.size()) || !m_ibseb[lev]) { return; }
149  const double t_wall0 = ParallelDescriptor::second();
150  m_ibseb[lev]->compute_shortwave(time);
151  m_ibseb[lev]->compute_longwave(cons);
152  // The ground surface layer's fields and the mixed-layer depth
153  // for the wall function beyond neutral (all null / zero unless asked).
154  const MultiFab* olen2d = nullptr;
155  const MultiFab* pblh2d = nullptr;
156  Real z_i_bulk = 0.0;
157  // The surface layer now exists per domain face. What the wall function wants
158  // here is the ground beneath the buildings, so take zlo; a surface layer on a
159  // lateral or upper wall says nothing about the stability of this column.
160  const auto& ground_sl = m_SurfaceLayer[Orientation(Direction::z, Orientation::low)];
161  if (ground_sl && ibseb_params.stability_correction) { olen2d = ground_sl->get_olen(lev); }
162  if (ibseb_params.convective_velocity == "deardorff") {
163  if (ground_sl && ground_sl->computes_pblh() && ibseb_params.z_i_mode == "pblh") {
164  pblh2d = ground_sl->get_pblh(lev);
165  }
166  z_i_bulk = (ibseb_params.z_i_mode == "fixed") ? ibseb_params.z_i
168  if (ibseb_params.debug) {
169  Print() << "[IBSEB DEBUG] lev=" << lev << " mixed-layer depth for w*: " << z_i_bulk << " m ("
170  << ibseb_params.z_i_mode << (pblh2d ? ", pblh per column" : "") << ")\n";
171  }
172  }
173  m_ibseb[lev]->compute_sensible(cons, xvel, yvel, zvel, solverChoice.c_p, olen2d, pblh2d, z_i_bulk);
174  if (ibseb_params.prognostic) {
175  m_ibseb[lev]->solve_balance(dt_lev);
176  } else {
177  m_ibseb[lev]->compute_ground(dt_lev);
178  }
179  m_ibseb[lev]->add_cost(ParallelDescriptor::second() - t_wall0);
180 }
amrex::Real ibseb_bulk_richardson_height(int lev, const amrex::MultiFab &cons, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel)
Definition: ERF_IBSEB.cpp:253
std::string convective_velocity
"none" or "deardorff"
Definition: ERF_IBSEBParams.H:135
bool prognostic
Definition: ERF_IBSEBParams.H:113
std::string z_i_mode
"bulk_ri", "pblh" or "fixed"
Definition: ERF_IBSEBParams.H:137
bool debug
[IBSEB DEBUG] prints: set description at build, per-step summary, per-building rows
Definition: ERF_IBSEBParams.H:32
amrex::Real z_i
Fixed mixed-layer depth [m] (z_i_mode = fixed, and the fallback)
Definition: ERF_IBSEBParams.H:138
bool stability_correction
Definition: ERF_IBSEBParams.H:87
amrex::Real c_p
Specific heat at constant pressure for dry air [J/(kg-K)].
Definition: ERF_DataStruct.H:2061

◆ ibseb_bulk_richardson_height()

Real ERF::ibseb_bulk_richardson_height ( int  lev,
const amrex::MultiFab &  cons,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel 
)

Mixed-layer depth of a level by the bulk Richardson method on the horizontal-mean profile (Troen and Mahrt; Vogelezang and Holtslag).

With the first level as the reference, Ri_b(z) = g (z - z_1) (theta(z)

  • theta_1) / (theta_1 (|U(z) - U_1|^2 + 100 u*^2)) with u* = 0.1 m/s, and the depth is the first cell centre where it exceeds erf.ibseb.ri_crit, or the domain depth when it never does (a neutral profile); both are heights above the domain bottom. The profile is the plane average of the conserved state and the face velocities, uniform vertical spacing assumed as elsewhere in the balance; called once per step and level when the convective velocity scale is on and z_i is not fixed, also as the fallback of the pblh mode.

Parameters
[in]levAMR level whose horizontal-mean profile is taken.
[in]consConserved state of the level; the Rho_comp and RhoTheta_comp averages give the mean potential temperature.
[in]xvelFace-centred x velocity, for the mean wind profile.
[in]yvelFace-centred y velocity, for the mean wind profile.
Returns
Mixed-layer depth above the domain bottom [m]; the depth of the domain when Ri_b never exceeds erf.ibseb.ri_crit.

254 {
255  MultiFab c2(cons, make_alias, Rho_comp, 2); // rho and rho theta are the first two components
256  PlaneAverage r_ave(&c2, geom[lev], 2);
257  r_ave.compute_averages(ZDir(), r_ave.field());
258  PlaneAverage u_ave(&xvel, geom[lev], 2);
259  u_ave.compute_averages(ZDir(), u_ave.field());
260  PlaneAverage v_ave(&yvel, geom[lev], 2);
261  v_ave.compute_averages(ZDir(), v_ave.field());
262  const int nz = r_ave.ncell_line();
263  Gpu::HostVector<Real> rho(nz), rth(nz), uu(u_ave.ncell_line()), vv(v_ave.ncell_line());
264  r_ave.line_average(0, rho);
265  r_ave.line_average(1, rth);
266  u_ave.line_average(0, uu);
267  v_ave.line_average(0, vv);
268  const Real dz = geom[lev].CellSize(2);
269  const Real z_top = geom[lev].ProbHi(2) - geom[lev].ProbLo(2); // depth of the domain
270  const Real th1 = rth[0] / rho[0];
271  const Real ustar_floor2 = 100.0 * 0.1 * 0.1;
272  Real z_i = z_top;
273  for (int k = 1; k < nz; ++k) {
274  const Real th = rth[k] / rho[k];
275  // |U(z) - U_1|^2 of the wind vector, so a veering wind of constant speed
276  // still counts as shear.
277  const Real dU2 = (uu[k] - uu[0]) * (uu[k] - uu[0]) + (vv[k] - vv[0]) * (vv[k] - vv[0]);
278  const Real rib = CONST_GRAV * (k * dz) * (th - th1) / (th1 * (dU2 + ustar_floor2));
279  if (rib > ibseb_params.ri_crit) { z_i = (k + 0.5) * dz; break; }
280  }
281  return z_i;
282 }
real(c_double), parameter c2
Definition: ERF_module_model_constants.F90:35
amrex::Real ri_crit
Critical bulk Richardson number of the diagnostic.
Definition: ERF_IBSEBParams.H:139
Here is the call graph for this function:

◆ ibseb_report()

void ERF::ibseb_report ( int  nstep,
amrex::Real  time 
)

Periodic report from ERF::post_timestep(), with nstep the number of completed steps (the plotfiles' numbering; the initial state is the step-0 report of init_ibseb()): after every erf.ibseb.csv_int-th step, print the summary of each level and append its CSV rows. A non-positive interval disables both.

Parameters
[in]nstepNumber of completed steps, tested against erf.ibseb.csv_int and written to the CSV rows.
[in]timeSimulation time at the end of the step [s], written to the summary and the CSV rows.
219 {
220  if (!ibseb_params.enable) { return; }
221  // With debug on the summary is printed every step, as the fire module
222  // does; the CSV rows keep their interval.
223  const bool csv_now = (ibseb_params.csv_int > 0) && (nstep % ibseb_params.csv_int == 0);
224  if (!csv_now && !ibseb_params.debug) { return; }
225  for (int lev = 0; lev <= finest_level && lev < static_cast<int>(m_ibseb.size()); ++lev) {
226  if (m_ibseb[lev]) { m_ibseb[lev]->report(time, nstep, csv_now); }
227  }
228 }
int csv_int
Steps between report rows; <= 0 disables.
Definition: ERF_IBSEBParams.H:31

◆ ibseb_write_checkpoint()

void ERF::ibseb_write_checkpoint ( const std::string &  checkpointname,
int  lev 
) const

Write the face state of one level into the checkpoint as IBSEBState, a field on the blocks around the buildings (IBFaceSet::state_boxarray()) clipped to the cells that own faces, so it scales with the shell of the buildings rather than with the level. Called inside the level loop of ERF::WriteCheckpointFile(); a no-op unless the balance is on and the level has faces.

Parameters
[in]checkpointnamePath of the checkpoint directory being written.
[in]levAMR level whose face state is written, as the IBSEBState field of its Level_ group.
196 {
197  if (!ibseb_params.enable || lev >= static_cast<int>(m_ibseb.size()) || !m_ibseb[lev]) { return; }
198  // A level without faces has no field to write (and nothing to restore).
199  if (!m_ibseb[lev]->has_state()) { return; }
200  MultiFab state = m_ibseb[lev]->make_state();
201  m_ibseb[lev]->save_state(state);
202  VisMF::Write(state, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "IBSEBState"));
203 }

◆ 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

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

◆ init1DArrays()

void ERF::init1DArrays ( )
private

◆ init_bcs()

void ERF::init_bcs ( )
private

Initialize ERF physical and component boundary condition data.

322 {
323  bool read_prim_theta = true;
324  bool use_surfacelayer = false;
325 
326  init_phys_bcs(read_prim_theta);
327 
328  // Deardorff LES, RANS, and PBL models consume lower-z SurfaceLayer fields
329  // explicitly. They cannot safely coexist with a lateral or upper
330  // SurfaceLayer wall. Smagorinsky LES uses the generic all-face diffusion
331  // path.
332  bool has_non_zlo_surface_layer = false;
333  for (OrientationIter oit; oit; ++oit) {
334  const Orientation ori = oit();
335  const bool is_zlo = (ori.coordDir() == static_cast<int>(Direction::z) &&
336  ori.faceDir() == Orientation::low);
337  if (!is_zlo && phys_bc_type[ori] == ERF_BC::surface_layer) {
338  has_non_zlo_surface_layer = true;
339  break;
340  }
341  }
342 
343  if (has_non_zlo_surface_layer) {
344  for (int lev = 0; lev <= max_level; ++lev) {
345  const auto& turb_choice = solverChoice.turbChoice[lev];
346  if (turb_choice.les_type == LESType::Deardorff ||
347  turb_choice.rans_type != RANSType::None ||
348  turb_choice.pbl_type != PBLType::None) {
349  Abort("Deardorff LES, RANS, and PBL models (including SHOC) support "
350  "SurfaceLayer only at zlo. Remove non-zlo surface_layer boundaries "
351  "or select Smagorinsky LES.");
352  }
353  }
354  }
355 
356  bool keqn_dir = (solverChoice.turbChoice[max_level].rans_type == RANSType::kEqn &&
357  solverChoice.turbChoice[max_level].dirichlet_k == true);
358  if (keqn_dir) {
359  // The wall value of k (AL01 Eq. 16) is written into the first cell by
360  // SurfaceLayer::update_fluxes and held there through every RK stage
361  // (erf_slow_rhs_post, ImplicitDiffForStateLU_*). The logical BC for
362  // RhoKE at the wall stays foextrap, so the ghost cell carries the same
363  // value and the surface-layer branch of the diffusion sets a zero flux
364  // through the wall face. Assume for now that all levels are RANS.
365  for (int lev = 0; lev < max_level; ++lev) {
366  if (solverChoice.turbChoice[lev].rans_type != RANSType::kEqn) {
367  Error("If using one-eqn RANS, all levels must be RANS for now");
368  }
369  }
370  // The AL01 Eq. 16 wall value is computed only by SurfaceLayer::update_fluxes,
371  // so without a surface layer at zlo nothing ever writes it. The pin in
372  // erf_slow_rhs_post is gated on SurfLayer != nullptr and would silently do
373  // nothing, while ImplicitDiffForStateLU_* would still collapse the klo row
374  // and freeze the first cell at whatever it was initialized to. Rather than
375  // let the two paths disagree, require the surface layer.
376  if (phys_bc_type[Orientation(Direction::z,Orientation::low)] != ERF_BC::surface_layer) {
377  Error("erf.dirichlet_k = true requires zlo.type = surface_layer: the wall value "
378  "of k (Axell & Liungman Eq. 16) is computed by the surface layer model");
379  }
380  Print() << "Using dirichlet wall value for the k equation (held in the first cell)" << std::endl;
381  }
382 
383  // k-eqn RANS under a surface layer without the Dirichlet wall value: the
384  // first cell cannot resolve the near-wall shear production, so k there
385  // settles at about half the AL01 equilibrium u*^2/Cmu0^2 (the mean wind
386  // still follows the log law because MOST supplies the stress). Warn.
387  for (int lev = 0; lev <= max_level; ++lev) {
388  if (solverChoice.turbChoice[lev].rans_type == RANSType::kEqn &&
389  !solverChoice.turbChoice[lev].dirichlet_k &&
390  phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer) {
391  Warning("erf.rans_type = kEqn with zlo.type = surface_layer but erf.dirichlet_k = false: "
392  "near-wall TKE will be about half the Axell & Liungman equilibrium value; "
393  "set erf.dirichlet_k = true");
394  }
395  }
396 
397  // *****************************************************************************
398  //
399  // Here we translate the physical boundary conditions -- one type per face --
400  // into logical boundary conditions for each velocity component
401  //
402  // *****************************************************************************
403  {
404  domain_bcs_type.resize(AMREX_SPACEDIM+NBCVAR_max);
405  domain_bcs_type_d.resize(AMREX_SPACEDIM+NBCVAR_max);
406 
407  for (OrientationIter oit; oit; ++oit) {
408  Orientation ori = oit();
409  int dir = ori.coordDir();
410  Orientation::Side side = ori.faceDir();
411  auto const bct = phys_bc_type[ori];
412  if ( bct == ERF_BC::symmetry )
413  {
414  if (side == Orientation::low) {
415  for (int i = 0; i < AMREX_SPACEDIM; i++) {
417  }
419  } else {
420  for (int i = 0; i < AMREX_SPACEDIM; i++) {
422  }
424  }
425  }
426  else if (bct == ERF_BC::outflow or bct == ERF_BC::ho_outflow )
427  {
428  if (side == Orientation::low) {
429  for (int i = 0; i < AMREX_SPACEDIM; i++) {
431  }
432  if (!solverChoice.anelastic[0]) {
434  }
435  } else {
436  for (int i = 0; i < AMREX_SPACEDIM; i++) {
438  }
439  if (!solverChoice.anelastic[0]) {
441  }
442  }
443  }
444  else if (bct == ERF_BC::open)
445  {
446  if (side == Orientation::low) {
447  for (int i = 0; i < AMREX_SPACEDIM; i++)
449  } else {
450  for (int i = 0; i < AMREX_SPACEDIM; i++)
452  }
453  }
454  else if (bct == ERF_BC::inflow)
455  {
456  if (side == Orientation::low) {
457  for (int i = 0; i < AMREX_SPACEDIM; i++) {
459  if (input_bndry_planes && dir < 2 && m_r2d->ingested_velocity()) {
461  }
462  }
463  } else {
464  for (int i = 0; i < AMREX_SPACEDIM; i++) {
466  if (input_bndry_planes && dir < 2 && m_r2d->ingested_velocity()) {
468  }
469  }
470  }
471  }
472  else if (bct == ERF_BC::inflow_outflow)
473  {
474  if (side == Orientation::low) {
475  for (int i = 0; i < AMREX_SPACEDIM; i++) {
477  }
478  } else {
479  for (int i = 0; i < AMREX_SPACEDIM; i++) {
481  }
482  }
483  }
484  else if (bct == ERF_BC::no_slip_wall)
485  {
486  if (side == Orientation::low) {
487  for (int i = 0; i < AMREX_SPACEDIM; i++) {
489  }
490  } else {
491  for (int i = 0; i < AMREX_SPACEDIM; i++) {
493  }
494  }
495  }
496  else if (bct == ERF_BC::slip_wall)
497  {
498  if (side == Orientation::low) {
499  for (int i = 0; i < AMREX_SPACEDIM; i++) {
501  }
502  // Only normal direction has ext_dir
504 
505  } else {
506  for (int i = 0; i < AMREX_SPACEDIM; i++) {
508  }
509  // Only normal direction has ext_dir
511  }
512  }
513  else if (bct == ERF_BC::periodic)
514  {
515  if (side == Orientation::low) {
516  for (int i = 0; i < AMREX_SPACEDIM; i++) {
518  }
519  } else {
520  for (int i = 0; i < AMREX_SPACEDIM; i++) {
522  }
523  }
524  }
525  else if ( bct == ERF_BC::surface_layer )
526  {
527  use_surfacelayer = true;
528  if (side == Orientation::low) {
529  for (int i = 0; i < AMREX_SPACEDIM; i++) {
531  }
532  // Only normal direction has ext_dir
534  } else {
535  for (int i = 0; i < AMREX_SPACEDIM; i++) {
537  }
538  // Only normal direction has ext_dir
540  }
541  }
542  }
543  }
544 
545  // *****************************************************************************
546  //
547  // Here we translate the physical boundary conditions -- one type per face --
548  // into logical boundary conditions for each cell-centered variable
549  // (including the base state variables)
550  // NOTE: all "scalars" share the same type of boundary condition
551  //
552  // *****************************************************************************
553  {
554  auto wall_scalar_type = [this] (int bc_comp, Orientation ori) {
555  const auto intent = m_wall_scalar_bc_intent[bc_comp][ori];
557  return ERFBCType::ext_dir;
561  return ERFBCType::neumann;
562  }
563  return ERFBCType::foextrap;
564  };
565 
566  for (OrientationIter oit; oit; ++oit) {
567  Orientation ori = oit();
568  int dir = ori.coordDir();
569  Orientation::Side side = ori.faceDir();
570  auto const bct = phys_bc_type[ori];
571  if ( bct == ERF_BC::symmetry )
572  {
573  if (side == Orientation::low) {
574  for (int i = 0; i < NBCVAR_max; i++) {
576  }
577  } else {
578  for (int i = 0; i < NBCVAR_max; i++) {
580  }
581  }
582  }
583  else if ( bct == ERF_BC::outflow )
584  {
585  if (side == Orientation::low) {
586  for (int i = 0; i < NBCVAR_max; i++) {
588  }
589  } else {
590  for (int i = 0; i < NBCVAR_max; i++) {
592  }
593  }
594  }
595  else if ( bct == ERF_BC::ho_outflow )
596  {
597  if (side == Orientation::low) {
598  for (int i = 0; i < NBCVAR_max; i++) {
600  }
601  } else {
602  for (int i = 0; i < NBCVAR_max; i++) {
604  }
605  }
606  }
607  else if ( bct == ERF_BC::open )
608  {
609  if (side == Orientation::low) {
610  for (int i = 0; i < NBCVAR_max; i++)
612  } else {
613  for (int i = 0; i < NBCVAR_max; i++)
615  }
616  }
617  else if ( bct == ERF_BC::no_slip_wall )
618  {
619  if (side == Orientation::low) {
620  for (int i = 0; i < NBCVAR_max; i++) {
622  domain_bcs_type[BCVars::cons_bc+i].setLo(dir, wall_scalar_type(BCVars::cons_bc+i, ori));
623  }
624  } else {
625  for (int i = 0; i < NBCVAR_max; i++) {
627  domain_bcs_type[BCVars::cons_bc+i].setHi(dir, wall_scalar_type(BCVars::cons_bc+i, ori));
628  }
629  }
630  }
631  else if (bct == ERF_BC::slip_wall)
632  {
633  if (side == Orientation::low) {
634  for (int i = 0; i < NBCVAR_max; i++) {
636  domain_bcs_type[BCVars::cons_bc+i].setLo(dir, wall_scalar_type(BCVars::cons_bc+i, ori));
637  }
638  } else {
639  for (int i = 0; i < NBCVAR_max; i++) {
641  domain_bcs_type[BCVars::cons_bc+i].setHi(dir, wall_scalar_type(BCVars::cons_bc+i, ori));
642  }
643  }
644  }
645  else if (bct == ERF_BC::inflow)
646  {
647  if (side == Orientation::low) {
648  for (int i = 0; i < NBCVAR_max; i++) {
650  if ((BCVars::cons_bc+i == RhoTheta_comp) &&
651  (th_bc_data[0].data() != nullptr))
652  {
653  if (read_prim_theta) domain_bcs_type[BCVars::cons_bc+i].setLo(dir, ERFBCType::ext_dir_prim);
654  }
655  else if (input_bndry_planes && dir < 2 && (
656  ( (BCVars::cons_bc+i == BCVars::Rho_bc_comp) && m_r2d->ingested_density()) ||
657  ( (BCVars::cons_bc+i == BCVars::RhoTheta_bc_comp) && m_r2d->ingested_theta() ) ||
658  ( (BCVars::cons_bc+i == BCVars::RhoKE_bc_comp) && m_r2d->ingested_KE() ) ||
659  ( (BCVars::cons_bc+i == BCVars::RhoScalar_bc_comp) && m_r2d->ingested_scalar() ) ||
660  ( (BCVars::cons_bc+i == BCVars::RhoQ1_bc_comp) && m_r2d->ingested_q1() ) ||
661  ( (BCVars::cons_bc+i == BCVars::RhoQ2_bc_comp) && m_r2d->ingested_q2() )) )
662  {
664  }
665  else if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
667  }
668  }
669  // Non-reflecting: extrapolate RhoTheta from interior so
670  // pressure is not prescribed, allowing acoustic waves out
671  if (m_bc_nonreflecting[ori]) {
673  }
674  } else {
675  for (int i = 0; i < NBCVAR_max; i++) {
677  if ((BCVars::cons_bc+i == RhoTheta_comp) &&
678  (th_bc_data[0].data() != nullptr))
679  {
680  if (read_prim_theta) domain_bcs_type[BCVars::cons_bc+i].setHi(dir, ERFBCType::ext_dir_prim);
681  }
682  else if (input_bndry_planes && dir < 2 && (
683  ( (BCVars::cons_bc+i == BCVars::Rho_bc_comp) && m_r2d->ingested_density()) ||
684  ( (BCVars::cons_bc+i == BCVars::RhoTheta_bc_comp) && m_r2d->ingested_theta() ) ||
685  ( (BCVars::cons_bc+i == BCVars::RhoKE_bc_comp) && m_r2d->ingested_KE() ) ||
686  ( (BCVars::cons_bc+i == BCVars::RhoScalar_bc_comp) && m_r2d->ingested_scalar() ) ||
687  ( (BCVars::cons_bc+i == BCVars::RhoQ1_bc_comp) && m_r2d->ingested_q1() ) ||
688  ( (BCVars::cons_bc+i == BCVars::RhoQ2_bc_comp) && m_r2d->ingested_q2() )
689  ) )
690  {
692  }
693  else if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
695  }
696  }
697  // Non-reflecting: extrapolate RhoTheta from interior
698  if (m_bc_nonreflecting[ori]) {
700  }
701  }
702  }
703  else if (bct == ERF_BC::inflow_outflow )
704  {
705  if (side == Orientation::low) {
706  for (int i = 0; i < NBCVAR_max; i++) {
708  if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
710  }
711  }
712  } else {
713  for (int i = 0; i < NBCVAR_max; i++) {
715  if (m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] == 0) {
717  }
718  }
719  }
720  }
721  else if (bct == ERF_BC::periodic)
722  {
723  if (side == Orientation::low) {
724  for (int i = 0; i < NBCVAR_max; i++) {
726  }
727  } else {
728  for (int i = 0; i < NBCVAR_max; i++) {
730  }
731  }
732  }
733  else if ( bct == ERF_BC::surface_layer )
734  {
735  if (side == Orientation::low) {
736  for (int i = 0; i < NBCVAR_max; i++) {
738  }
739  } else {
740  for (int i = 0; i < NBCVAR_max; i++) {
742  }
743  }
744  // NOTE: with erf.dirichlet_k the RhoKE wall value lives in the
745  // first cell (see above); foextrap is the right logical BC.
746  }
747  }
748  }
749 
750  // Sanity check that implicit diffusion is consistent with the BC types.
751  // Turn off implicit diffusion for a component if its BCs don't match
752  // those allowed by the tridiagonal solver.
753  const BCRec* bc_ptr = domain_bcs_type.data();
755  Vector<std::string> cc_comp_name = {"Theta", "KE", "Qv"};
756  for (int icomp(0); icomp < cc_comp_map.size(); ++icomp) {
757  int bc_comp = cc_comp_map[icomp];
758  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
759  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
760  bool neumann_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::neumann);
761  bool neumann_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::neumann);
762  if ( (!foextrap_on_zlo && !neumann_on_zlo && !use_surfacelayer) ||
763  (!foextrap_on_zhi && !neumann_on_zhi) ) {
764  Print() << "WARNING: Selected BCs for " << cc_comp_name[icomp] << " are not supported with implicit diffusion. Turning off implicit diffusion for this component." << "\n";
766  if (bc_comp == BCVars::RhoKE_bc_comp) { solverChoice.implicit_ke_diffusion = false; }
768  }
769  }
770  Vector<int> vel_comp_map = {BCVars::xvel_bc, BCVars::yvel_bc};
771  Vector<std::string> vel_comp_name = {"xvel", "yvel"};
772  for (int icomp(0); icomp < vel_comp_map.size(); ++icomp) {
773  int bc_comp = vel_comp_map[icomp];
774  bool ext_dir_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir ||
775  bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim);
776  bool ext_dir_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir ||
777  bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim);
778  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
779  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
780  if ( (!foextrap_on_zlo && !ext_dir_on_zlo && !use_surfacelayer) ||
781  (!foextrap_on_zhi && !ext_dir_on_zhi) ) {
782  Print() << "WARNING: Selected BCs for " << vel_comp_name[icomp] << " are not supported with implicit diffusion. Turning off implicit diffusion for this component." << "\n";
784  }
785  }
786 
787  // NOTE: Gpu:copy is a wrapper to htod_memcpy (GPU) or memcpy (CPU) and is a blocking comm
788  Gpu::copy(Gpu::hostToDevice, domain_bcs_type.begin(), domain_bcs_type.end(), domain_bcs_type_d.begin());
789 }
#define NBCVAR_max
Definition: ERF_IndexDefines.H:32
@ ho_outflow
@ inflow_outflow
void init_phys_bcs(bool &read_prim_theta)
Definition: ERF_InitBCs.cpp:26
amrex::Array< bool, AMREX_SPACEDIM *2 > m_bc_nonreflecting
Definition: ERF.H:1448
amrex::Array< amrex::Array< erf_wall_scalar_bc::WallScalarBCIntent, AMREX_SPACEDIM *2 >, NBCVAR_max > m_wall_scalar_bc_intent
Definition: ERF.H:1445
@ RhoQ1_bc_comp
Definition: ERF_IndexDefines.H:94
@ RhoKE_bc_comp
Definition: ERF_IndexDefines.H:92
@ RhoTheta_bc_comp
Definition: ERF_IndexDefines.H:91
@ RhoQ2_bc_comp
Definition: ERF_IndexDefines.H:95
@ Rho_bc_comp
Definition: ERF_IndexDefines.H:90
@ neumann
Definition: ERF_IndexDefines.H:302
@ open
Definition: ERF_IndexDefines.H:304
@ reflect_odd
Definition: ERF_IndexDefines.H:293
@ hoextrap
Definition: ERF_IndexDefines.H:298
@ foextrap
Definition: ERF_IndexDefines.H:296
@ ext_dir
Definition: ERF_IndexDefines.H:297
@ ext_dir_prim
Definition: ERF_IndexDefines.H:300
@ ext_dir_upwind
Definition: ERF_IndexDefines.H:305
@ int_dir
Definition: ERF_IndexDefines.H:294
@ neumann_int
Definition: ERF_IndexDefines.H:303
@ reflect_even
Definition: ERF_IndexDefines.H:295
bool implicit_moisture_diffusion
Whether implicit vertical moisture diffusion is included.
Definition: ERF_DataStruct.H:2003
bool implicit_ke_diffusion
Whether implicit vertical TKE diffusion is included.
Definition: ERF_DataStruct.H:2004

◆ 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 
96  ParallelFor(xbx, ybx, zbx,
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.queryAdd("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  Vector<std::string> varnames = {"density","theta", "x_velocity","y_velocity","z_velocity", "qv", "qc", "qrain"};
160  MultiFab mf_cc_pert;
161  create_random_perturbations(lev, mf_cc_pert);
162  WriteSingleLevelPlotfile("plt_pert_correlated_gaussian", mf_cc_pert, varnames, geom[lev], zero, 0);
164  //WriteSingleLevelPlotfile("plt_pert_correlated_gaussian", mf_cc_pert, varnames, geom[lev], zero, 0);
165  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]);
166  }
167 }
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:732
void apply_gaussian_smoothing_to_perturbations(const int lev, amrex::MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:117
void ReadVelsOnlyFromCheckpointFile(int lev_to_fill, std::string &chkfile)
Definition: ERF_Checkpoint.cpp:1731
void create_random_perturbations(const int lev, amrex::MultiFab &mf_cc_pert)
Definition: ERF_InitForEnsemble.cpp:33
eb_aux_ const * get_v_const_factory() const noexcept
Return the ERF auxiliary y-face EB factory.
Definition: ERF_EB.H:131
eb_aux_ const * get_u_const_factory() const noexcept
Return the ERF auxiliary x-face EB factory.
Definition: ERF_EB.H:129
amrex::Vector< int > fixed_density
Per-level flag selecting fixed-density treatment.
Definition: ERF_DataStruct.H:1983
bool is_init_for_ensemble
Whether initialization is for an ensemble simulation.
Definition: ERF_DataStruct.H:2323
Here is the call graph for this function:

◆ init_Dirichlet_bc_data()

void ERF::init_Dirichlet_bc_data ( const std::string  input_file)
private

Read vertical Dirichlet boundary data and interpolate it to ERF levels.

Parameters
input_filePath to the Dirichlet input profile file
797 {
798  // Read the dirichlet_input file
799  Print() << "dirichlet_input file location : " << input_file << std::endl;
800  std::ifstream input_reader(input_file);
801  if (!input_reader.is_open()) {
802  amrex::Abort("Error opening the dirichlet_input file.\n");
803  }
804 
805  Print() << "Successfully opened the dirichlet_input file. Now reading... " << std::endl;
806  std::string line;
807 
808  // Size of Ninp (number of z points in input file)
809  Vector<Real> z_inp_tmp, u_inp_tmp, v_inp_tmp, w_inp_tmp, th_inp_tmp;
810 
811  // Top and bot for domain
812  const int klo = geom[0].Domain().smallEnd()[2];
813  const int khi = geom[0].Domain().bigEnd()[2];
814  const Real zbot = zlevels_stag[0][klo];
815  const Real ztop = zlevels_stag[0][khi+1];
816 
817  // Flag if theta input
818  Real th_init = -Real(300.0);
819  bool th_read{false};
820 
821  // Add surface
822  z_inp_tmp.push_back(zbot); // height above sea level [m]
823  u_inp_tmp.push_back(zero);
824  v_inp_tmp.push_back(zero);
825  w_inp_tmp.push_back(zero);
826  th_inp_tmp.push_back(th_init);
827 
828  // Read the vertical profile at each given height
829  Real z, u, v, w, th;
830  while(std::getline(input_reader, line)) {
831  std::istringstream iss_z(line);
832 
833  Vector<Real> rval_v;
834  Real rval;
835  while (iss_z >> rval) {
836  rval_v.push_back(rval);
837  }
838  if ((rval_v.size() != 4) && (rval_v.size() != 5)) {
839  Abort("Unknown inflow file format!");
840  }
841  z = rval_v[0];
842  u = rval_v[1];
843  v = rval_v[2];
844  w = rval_v[3];
845 
846  // Format without theta
847  if (rval_v.size() == 4) {
848  if (z == zbot) {
849  u_inp_tmp[0] = u;
850  v_inp_tmp[0] = v;
851  w_inp_tmp[0] = w;
852  } else {
853  AMREX_ALWAYS_ASSERT(z > z_inp_tmp[z_inp_tmp.size()-1]); // sounding is increasing in height
854  z_inp_tmp.push_back(z);
855  u_inp_tmp.push_back(u);
856  v_inp_tmp.push_back(v);
857  w_inp_tmp.push_back(w);
858  if (z >= ztop) break;
859  }
860  } else if (rval_v.size() == 5) {
861  th_read = true;
862  th = rval_v[4];
863  if (z == zbot) {
864  u_inp_tmp[0] = u;
865  v_inp_tmp[0] = v;
866  w_inp_tmp[0] = w;
867  th_inp_tmp[0] = th;
868  } else {
869  AMREX_ALWAYS_ASSERT(z > z_inp_tmp[z_inp_tmp.size()-1]); // sounding is increasing in height
870  z_inp_tmp.push_back(z);
871  u_inp_tmp.push_back(u);
872  v_inp_tmp.push_back(v);
873  w_inp_tmp.push_back(w);
874  th_inp_tmp.push_back(th);
875  if (z >= ztop) break;
876  }
877  } else {
878  Abort("Unknown inflow file format!");
879  }
880  }
881 
882  // Ensure we set a reasonable theta surface
883  if (th_read) {
884  if (th_inp_tmp[0] == th_init) {
885  AMREX_ALWAYS_ASSERT_WITH_MESSAGE((th_inp_tmp.size() > 2) && (z_inp_tmp.size() > 2),
886  "Need at least 3 theta profile points to extrapolate surface theta");
887  Real slope = (th_inp_tmp[2] - th_inp_tmp[1]) / (z_inp_tmp[2] - z_inp_tmp[1]);
888  Real dz = z_inp_tmp[0] - z_inp_tmp[1];
889  th_inp_tmp[0] = slope * dz + th_inp_tmp[1];
890  }
891  }
892 
893  amrex::Print() << "Successfully read and interpolated the dirichlet_input file..." << std::endl;
894  input_reader.close();
895 
896  for (int lev = 0; lev <= max_level; lev++) {
897 
898  const int Nz = geom[lev].Domain().size()[2];
899 
900  // Size of Nz (domain grid)
901  Vector<Real> zcc_inp(Nz );
902  Vector<Real> znd_inp(Nz+1);
903  Vector<Real> u_inp(Nz ); xvel_bc_data[lev].resize(Nz ,zero);
904  Vector<Real> v_inp(Nz ); yvel_bc_data[lev].resize(Nz ,zero);
905  Vector<Real> w_inp(Nz+1); zvel_bc_data[lev].resize(Nz+1,zero);
906  Vector<Real> th_inp;
907  if (th_read) {
908  th_inp.resize(Nz);
909  th_bc_data[lev].resize(Nz, zero);
910  }
911 
912  // At this point, we have an input from zbot up to
913  // z_inp_tmp[N-1] >= ztop. Now, interpolate to grid level 0 heights
914  const int Ninp = z_inp_tmp.size();
915  for (int k(0); k<Nz; ++k) {
916  zcc_inp[k] = myhalf * (zlevels_stag[lev][k] + zlevels_stag[lev][k+1]);
917  znd_inp[k] = zlevels_stag[lev][k];
918  u_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), u_inp_tmp.dataPtr(), zcc_inp[k], Ninp);
919  v_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), v_inp_tmp.dataPtr(), zcc_inp[k], Ninp);
920  w_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), w_inp_tmp.dataPtr(), znd_inp[k], Ninp);
921  if (th_read) {
922  th_inp[k] = interpolate_1d(z_inp_tmp.dataPtr(), th_inp_tmp.dataPtr(), zcc_inp[k], Ninp);
923  }
924  }
925  znd_inp[Nz] = ztop;
926  w_inp[Nz] = interpolate_1d(z_inp_tmp.dataPtr(), w_inp_tmp.dataPtr(), ztop, Ninp);
927 
928  // Copy host data to the device
929  Gpu::copy(Gpu::hostToDevice, u_inp.begin(), u_inp.end(), xvel_bc_data[lev].begin());
930  Gpu::copy(Gpu::hostToDevice, v_inp.begin(), v_inp.end(), yvel_bc_data[lev].begin());
931  Gpu::copy(Gpu::hostToDevice, w_inp.begin(), w_inp.end(), zvel_bc_data[lev].begin());
932  if (th_read) {
933  Gpu::copy(Gpu::hostToDevice, th_inp.begin(), th_inp.end(), th_bc_data[lev].begin());
934  }
935 
936  // NOTE: These device vectors are passed to the PhysBC constructors when that
937  // class is instantiated in ERF_MakeNewArrays.cpp.
938  } // lev
939 }
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
96 {
97  // We only want to read the file once -- here we fill one FArrayBox (per variable) that spans the domain
98  if (lev == 0) {
100  Error("input_sounding file name must be provided via input");
101  }
102 
104 
105  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
106 
107  // this will interpolate the input profiles to the nominal height levels
108  // (ranging from 0 to the domain top)
109  for (int n = 0; n < input_sounding_data.n_sounding_files; n++) {
110  input_sounding_data.read_from_file(geom[lev], zlevels_stag[lev], n, is_moist);
111  }
112 
113  //input_sounding_data.set_start_time(start_time);
114 
115  // this will calculate the hydrostatically balanced density and pressure
116  // profiles following WRF ideal.exe
117  if (solverChoice.sounding_type == SoundingType::Ideal) {
119  } else if (solverChoice.sounding_type == SoundingType::Isentropic ||
120  solverChoice.sounding_type == SoundingType::DryIsentropic) {
121  input_sounding_data.assume_dry = (solverChoice.sounding_type == SoundingType::DryIsentropic);
123  }
124 
125  } else {
126  //
127  // We need to do this interp from coarse level in order to set the values of
128  // the base state inside the domain but outside of the fine region
129  //
130  base_state[lev-1].FillBoundary(geom[lev-1].periodicity());
131  //
132  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
133  // have been pre-filled - this includes ghost cells both inside and outside
134  // the domain
135  //
136  InterpFromCoarseLevel(base_state[lev], base_state[lev].nGrowVect(),
137  IntVect(0,0,0), // do not fill ghost cells outside the domain
138  base_state[lev-1], 0, 0, base_state[lev].nComp(),
139  geom[lev-1], geom[lev],
140  refRatio(lev-1), &cell_cons_interp,
142 
143  // We need to do this here because the interpolation above may leave corners unfilled
144  // when the corners need to be filled by, for example, reflection of the fine ghost
145  // cell outside the fine region but inide the domain.
146  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
147  }
148 
149  auto& lev_new = vars_new[lev];
150 
151  const bool l_isentropic = (solverChoice.sounding_type == SoundingType::Isentropic ||
152  solverChoice.sounding_type == SoundingType::DryIsentropic);
153 
154  const bool constant_density_sounding = (solverChoice.sounding_type == SoundingType::ConstantDensity);
155 
156  MultiFab r_hse (base_state[lev], make_alias, BaseState::r0_comp, 1);
157  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
158  MultiFab pi_hse(base_state[lev], make_alias, BaseState::pi0_comp, 1);
159  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
160  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp, 1);
161 
162  const Real l_gravity = solverChoice.gravity;
163  const Real l_rdOcp = solverChoice.rdOcp;
164  const bool l_moist = (solverChoice.moisture_type != MoistureType::None);
165 
166  int ngz = r_hse.nGrow(2);
167 
168 #ifdef _OPENMP
169 #pragma omp parallel if (Gpu::notInLaunchRegion())
170 #endif
171  for (MFIter mfi(lev_new[Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
172  const Box &bx = mfi.tilebox();
173  const auto &cons_arr = lev_new[Vars::cons].array(mfi);
174  Array4<Real> r_hse_arr = r_hse.array(mfi);
175  Array4<Real> p_hse_arr = p_hse.array(mfi);
176  Array4<Real> pi_hse_arr = pi_hse.array(mfi);
177  Array4<Real> th_hse_arr = th_hse.array(mfi);
178  Array4<Real> qv_hse_arr = qv_hse.array(mfi);
179 
180  Array4<Real const> z_cc_arr = (z_phys_cc[lev]) ? z_phys_cc[lev]->const_array(mfi) : Array4<Real const>{};
181 
182  if (constant_density_sounding) {
183  // This assumes rho_0 = one
184  // HSE will be calculated later with call to initHSE
185  init_state_from_input_sounding(bx, cons_arr, geom[lev].data(), z_cc_arr,
186  l_moist, input_sounding_data);
187  }
188  else
189  {
190  // HSE will be initialized here, interpolated from values previously
191  // calculated by calc_rho_p or calc_rho_p_isentropic
193  r_hse_arr, p_hse_arr, pi_hse_arr, th_hse_arr, qv_hse_arr,
194  geom[lev].data(), z_cc_arr,
195  l_gravity, l_rdOcp, l_moist, input_sounding_data,
196  l_isentropic, ngz);
197  }
198  }
199 
200  if (!constant_density_sounding) {
201  // Enforce HSE on the base state -- holding th_hse and qv_hse constant
202  bool maintain_Th = true;
203  rebalance_columns(r_hse, th_hse, qv_hse, qv_hse, z_phys_nd[lev].get(), geom[lev], maintain_Th);
204 
205  // Update rho in the state from base state
206  MultiFab::Copy(lev_new[Vars::cons], r_hse, 0, Rho_comp, 1, 1);
207 
208  // Update (rho theta) in the state from base state
209  MultiFab::Copy(lev_new[Vars::cons], th_hse, 0, RhoTheta_comp, 1, 1);
210  MultiFab::Multiply(lev_new[Vars::cons], lev_new[Vars::cons], Rho_comp, RhoTheta_comp, 1, 1);
211 
212  if (l_moist) {
213  // Update (rho qv) in the state from base state
214  MultiFab::Copy(lev_new[Vars::cons], qv_hse, 0, RhoQ1_comp, 1, 1);
215  MultiFab::Multiply(lev_new[Vars::cons], lev_new[Vars::cons], Rho_comp, RhoQ1_comp, 1, 1);
216  }
217  }
218 
219  for (MFIter mfi(lev_new[Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi)
220  {
221  const Box &bx = mfi.tilebox();
222  const auto& xvel_arr = lev_new[Vars::xvel].array(mfi);
223  const auto& yvel_arr = lev_new[Vars::yvel].array(mfi);
224  const auto& zvel_arr = lev_new[Vars::zvel].array(mfi);
225  const auto& z_nd_arr = (z_phys_nd[lev]) ? z_phys_nd[lev]->const_array(mfi) : Array4<Real const>{};
226  init_velocities_from_input_sounding(bx, xvel_arr, yvel_arr, zvel_arr,
227  geom[lev].data(), z_nd_arr, input_sounding_data);
228  }
229 
230  // *****************************************************************************
231  // Re-compute p_hse and pi_hse
232  // *****************************************************************************
233  for (MFIter mfi(lev_new[Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi)
234  {
235  Box bx = mfi.tilebox();
236 
237  const Array4<const Real>& r_hse_arr = r_hse.const_array(mfi);
238  const Array4<const Real>& th_hse_arr = th_hse.const_array(mfi);
239  const Array4<const Real>& qv_hse_arr = qv_hse.const_array(mfi);
240 
241  const Array4< Real>& p_hse_arr = p_hse.array(mfi);
242  const Array4< Real>& pi_hse_arr = pi_hse.array(mfi);
243 
244  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
245  {
246  Real rhotheta = r_hse_arr(i,j,k) * th_hse_arr(i,j,k);
247  p_hse_arr(i,j,k) = getPgivenRTh(rhotheta,qv_hse_arr(i,j,k));
248  pi_hse_arr(i,j,k) = getExnergivenRTh(rhotheta, l_rdOcp, qv_hse_arr(i,j,k));
249  });
250  }
251 }
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:326
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:457
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:265
void rebalance_columns(MultiFab &rho, MultiFab &theta, const MultiFab &qv, const MultiFab &qt, const MultiFab *z_phys, const Geometry &geom, const bool &maintain_Th, bool use_sfc)
Definition: ERF_Rebalance.cpp:21
void resize_arrays()
Resize host and device sounding arrays for the configured sounding times.
Definition: ERF_InputSoundingData.H:67
bool assume_dry
Whether to ignore moisture in isentropic pressure integration.
Definition: ERF_InputSoundingData.H:500
void calc_rho_p(int itime)
Hydrostatically integrate density and pressure for one sounding.
Definition: ERF_InputSoundingData.H:232
void calc_rho_p_isentropic(int itime)
Compute density and pressure for an isentropic sounding profile.
Definition: ERF_InputSoundingData.H:344
void read_from_file(const amrex::Geometry &geom, const amrex::Vector< amrex::Real > &zlevels_stag, int itime, bool is_moist)
Read and interpolate one input sounding profile onto level-0 heights.
Definition: ERF_InputSoundingData.H:91
amrex::Vector< std::string > input_sounding_file
Sounding file path for each configured sounding time.
Definition: ERF_InputSoundingData.H:495
int n_sounding_files
Number of input sounding files.
Definition: ERF_InputSoundingData.H:497
static SoundingType sounding_type
Interpretation used for input sounding profiles.
Definition: ERF_DataStruct.H:1946
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

Read and interpolate a geostrophic wind profile onto ERF vertical levels.

Parameters
input_filePath to the geostrophic wind profile
u_geosHost vector storing interpolated geostrophic x-wind
u_geos_dDevice vector storing interpolated geostrophic x-wind
v_geosHost vector storing interpolated geostrophic y-wind
v_geos_dDevice vector storing interpolated geostrophic y-wind
lgeomGeometry defining the vertical domain
zlev_stagOptional stretched-grid staggered height levels
28 {
29  const int klo = 0;
30  const int khi = lgeom.Domain().bigEnd()[AMREX_SPACEDIM-1];
31  const amrex::Real dz = lgeom.CellSize()[AMREX_SPACEDIM-1];
32 
33  const bool grid_stretch = (zlev_stag.size() > 0);
34  const Real zbot = (grid_stretch) ? zlev_stag[klo] : lgeom.ProbLo(AMREX_SPACEDIM-1);
35  const Real ztop = (grid_stretch) ? zlev_stag[khi+1] : lgeom.ProbHi(AMREX_SPACEDIM-1);
36 
37  amrex::Print() << "Reading geostrophic wind profile from " << input_file << std::endl;
38  std::ifstream profile_reader(input_file);
39  if(!profile_reader.is_open()) {
40  amrex::Error("Error opening the abl_geo_wind_table\n");
41  }
42 
43  // First, read the input data into temp vectors
44  std::string line;
45  Vector<Real> z_inp, Ug_inp, Vg_inp;
46  Real z, Ug, Vg;
47  amrex::Print() << "z Ug Vg" << std::endl;
48  while(std::getline(profile_reader, line)) {
49  std::istringstream iss(line);
50  iss >> z >> Ug >> Vg;
51  amrex::Print() << z << " " << Ug << " " << Vg << std::endl;
52  z_inp.push_back(z);
53  Ug_inp.push_back(Ug);
54  Vg_inp.push_back(Vg);
55  if (z >= ztop) break;
56  }
57 
58  const int Ninp = z_inp.size();
59  AMREX_ALWAYS_ASSERT(z_inp[0] <= zbot);
60  AMREX_ALWAYS_ASSERT(z_inp[Ninp-1] >= ztop);
61 
62  // Now, interpolate vectors to the cell centers
63  for (int k = 0; k <= khi; k++) {
64  z = (grid_stretch) ? myhalf * (zlev_stag[k] + zlev_stag[k+1])
65  : zbot + (k + myhalf) * dz;
66  u_geos[k] = interpolate_1d(z_inp.dataPtr(), Ug_inp.dataPtr(), z, Ninp);
67  v_geos[k] = interpolate_1d(z_inp.dataPtr(), Vg_inp.dataPtr(), z, Ninp);
68  }
69 
70  // Copy from host version to device version
71  Gpu::copy(Gpu::hostToDevice, u_geos.begin(), u_geos.end(), u_geos_d.begin());
72  Gpu::copy(Gpu::hostToDevice, v_geos.begin(), v_geos.end(), v_geos_d.begin());
73 
74  profile_reader.close();
75 }
Here is the call graph for this function:

◆ init_ibseb()

void ERF::init_ibseb ( )

Build the face set of every level from the blanking and, on a restart, refill its state from the checkpoint.

Called from ERF::InitData_post() after restart(), which is the first point where both paths (fresh start and restart) have the blanking of every level built and ghost-filled. The face list is always rebuilt from the blanking rather than read back, so the checkpoint carries only the state (IBSEBState, see IBFaceSet::state_ncomp()) and a restart on a different number of ranks works. A checkpoint from a run without the balance has no such field; the initial state is kept and a note is printed.

35 {
36  if (!ibseb_params.enable) { return; }
37  if (solverChoice.buildings_type != BuildingsType::ImmersedForcing) {
38  Abort("erf.ibseb.enable needs erf.buildings_type = ImmersedForcing");
39  }
40  // The face detection takes every solid column of the blanking for a
41  // building, so terrain by immersed forcing would be put under the
42  // balance as well; it is not supported.
43  if (solverChoice.terrain_type == TerrainType::ImmersedForcing) {
44  Abort("erf.ibseb.enable does not support erf.terrain_type = ImmersedForcing: "
45  "the balance runs on building faces only");
46  }
47  // The face areas, the face heights, the wall-function distance and the
48  // ray cast all take the level's constant cell sizes.
49  if (solverChoice.mesh_type != MeshType::ConstantDz) {
50  Abort("erf.ibseb.enable needs a uniform vertical grid (no erf.terrain_z_levels or stretched mesh): "
51  "the face geometry and the ray cast assume constant dz");
52  }
53  // The face list is built once here from the blanking; a regrid would
54  // leave it indexing the old boxes.
55  if (regrid_int > 0) {
56  Abort("erf.ibseb.enable does not support regridding (erf.regrid_int > 0): the face list is built once at initialisation");
57  }
58  // The immersed forcing's own surface-temperature conditions would fight
59  // the face balance for the same cells.
60  if (solverChoice.if_init_surf_temp > 0.0 ||
62  solverChoice.if_Olen_in != Real(1.e-8)) {
63  Abort("erf.ibseb.enable: remove erf.if_init_surf_temp, erf.if_surf_temp_flux and erf.if_Olen; "
64  "the face balance sets the temperature condition at the buildings");
65  }
66  m_ibseb.resize(finest_level + 1);
67  for (int lev = 0; lev <= finest_level; ++lev) {
68  m_ibseb[lev] = std::make_unique<IBFaceSet>(ibseb_params, lev);
69  const double t_init0 = ParallelDescriptor::second();
70  m_ibseb[lev]->build(*terrain_blanking[lev], geom[lev]);
71  std::unique_ptr<MultiFab> restored;
72  if (!restart_chkfile.empty()) {
73  const std::string name = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "IBSEBState");
74  if (FileExists(name + "_H")) {
75  // The field's width is n_slots x (2 + n_slab_layers) and its
76  // boxes follow the buildings; a checkpoint written with
77  // another layer count or another building set cannot be
78  // unpacked.
79  const VisMF header(name);
80  const int ncomp_chk = header.nComp();
81  if (ncomp_chk != m_ibseb[lev]->state_ncomp()) {
82  Abort("erf.ibseb: IBSEBState in " + restart_chkfile + " has " + std::to_string(ncomp_chk)
83  + " components; the deck sets erf.ibseb.n_slab_layers = " + std::to_string(ibseb_params.n_slab_layers)
84  + ", which with the " + std::to_string(m_ibseb[lev]->n_slots())
85  + " face slots per cell of this blanking needs " + std::to_string(m_ibseb[lev]->state_ncomp())
86  + ". The checkpoint was written with another erf.ibseb.n_slab_layers (restart with the"
87  " checkpoint's value) or for another building set.");
88  }
89  if (header.boxArray() != m_ibseb[lev]->state_boxarray()) {
90  Abort("erf.ibseb: IBSEBState in " + restart_chkfile + " was written for a different building layout ("
91  + std::to_string(header.boxArray().size()) + " boxes against the "
92  + std::to_string(m_ibseb[lev]->state_boxarray().size()) + " the blanking gives); restart from a checkpoint of the same buildings");
93  }
94  restored = std::make_unique<MultiFab>(m_ibseb[lev]->make_state());
95  VisMF::Read(*restored, name);
96  m_ibseb[lev]->load_state(*restored);
97  Print() << "[IBSEB] Face state restored from " << restart_chkfile << "\n";
98  } else {
99  Print() << "[IBSEB] Checkpoint has no IBSEBState; keeping the initial face state.\n";
100  }
101  }
102  m_ibseb[lev]->assign_materials();
103  m_ibseb[lev]->compute_view_fractions();
104  m_ibseb[lev]->set_init_cost(ParallelDescriptor::second() - t_init0);
105  // Initial diagnostics for the first report. On a restart they
106  // overwrite the sensible flux with a diagnostic value, so the
107  // checkpointed flux (which the convective velocity scale of the next
108  // step reads as the previous step's) is put back afterwards.
109  m_ibseb[lev]->compute_shortwave(t_new[lev]);
110  m_ibseb[lev]->compute_longwave(vars_new[lev][Vars::cons]);
111  m_ibseb[lev]->compute_sensible(vars_new[lev][Vars::cons], vars_new[lev][Vars::xvel],
113  if (restored) { m_ibseb[lev]->load_state(*restored); }
114  m_ibseb[lev]->report(t_new[lev], istep[lev], ibseb_params.csv_int > 0);
115  }
116 }
int regrid_int
Definition: ERF.H:1522
int n_slab_layers
Conduction layers per face.
Definition: ERF_IBSEBParams.H:27
amrex::Real if_Olen_in
Input Obukhov length for immersed-forcing MOST [m].
Definition: ERF_DataStruct.H:2046
amrex::Real if_init_surf_temp
Initial immersed-forcing surface temperature [K].
Definition: ERF_DataStruct.H:2044
amrex::Real if_surf_temp_flux
Immersed-forcing surface temperature flux [K m/s].
Definition: ERF_DataStruct.H:2043

◆ 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  MultiFab* terrain_blank_xface = terrain_blanking_xface[lev].get();
20  MultiFab* terrain_blank_yface = terrain_blanking_yface[lev].get();
21  MultiFab* terrain_blank_zface = terrain_blanking_zface[lev].get();
22 
23 #ifdef _OPENMP
24 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
25 #endif
26  for (MFIter mfi(lev_new[Vars::cons], TileNoZ()); mfi.isValid(); ++mfi)
27  {
28  const Box &xbx = mfi.tilebox(IntVect(1,0,0));
29  const Box &ybx = mfi.tilebox(IntVect(0,1,0));
30  const Box &zbx = mfi.tilebox(IntVect(0,0,1));
31  const Real epsilon = Real(1e-2);
32 
33  const Array4<const Real>& t_blank_arr = terrain_blank->const_array(mfi);
34  const Array4<const Real>& t_blank_xface_arr = (terrain_blank_xface) ? terrain_blank_xface->const_array(mfi) :
35  Array4<const Real>{};
36  const Array4<const Real>& t_blank_yface_arr = (terrain_blank_yface) ? terrain_blank_yface->const_array(mfi) :
37  Array4<const Real>{};
38  const Array4<const Real>& t_blank_zface_arr = (terrain_blank_zface) ? terrain_blank_zface->const_array(mfi) :
39  Array4<const Real>{};
40 
41  const auto &xvel_arr = lev_new[Vars::xvel].array(mfi);
42  const auto &yvel_arr = lev_new[Vars::yvel].array(mfi);
43  const auto &zvel_arr = lev_new[Vars::zvel].array(mfi);
44 
45  // Set the x,y,z-velocities
46  ParallelFor(xbx, ybx, zbx,
47  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
48  const Real t_blank = (t_blank_xface_arr) ? t_blank_xface_arr(i, j, k) :
49  myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i-1, j, k));
50  if (t_blank == one) { xvel_arr(i, j, k) = epsilon; }
51  },
52  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
53  const Real t_blank = (t_blank_yface_arr) ? t_blank_yface_arr(i, j, k) :
54  myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j-1, k));
55  if (t_blank == one) { yvel_arr(i, j, k) = epsilon; }
56  },
57  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
58  const Real t_blank = (t_blank_zface_arr) ? t_blank_zface_arr(i, j, k) :
59  myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j, k-1));
60  if (t_blank == one) { zvel_arr(i, j, k) = epsilon; }
61  });
62  } //mfi
63 }
Here is the call graph for this function:

◆ init_only()

void ERF::init_only ( int  lev,
double  time 
)
2426 {
2427  t_new[lev] = elapsed_time;
2428  t_old[lev] = elapsed_time - bogus_large_value;
2429 
2430  auto& lev_new = vars_new[lev];
2431  auto& lev_old = vars_old[lev];
2432 
2433  // Loop over grids at this level to initialize our grid data
2434  lev_new[Vars::cons].setVal(0.0); lev_old[Vars::cons].setVal(0.0);
2435  lev_new[Vars::xvel].setVal(0.0); lev_old[Vars::xvel].setVal(0.0);
2436  lev_new[Vars::yvel].setVal(0.0); lev_old[Vars::yvel].setVal(0.0);
2437  lev_new[Vars::zvel].setVal(0.0); lev_old[Vars::zvel].setVal(0.0);
2438 
2439  // Initialize background flow (optional)
2440  if (solverChoice.init_type == InitType::Input_Sounding) {
2441  // The physbc's need the terrain but are needed for initHSE
2442  // We have already made the terrain in the call to init_zphys
2443  // in MakeNewLevelFromScratch
2444  make_physbcs(lev);
2445 
2446  // Now init the base state and the data itself
2448 
2449  // The base state has been initialized by integrating vertically
2450  // through the sounding for ideal (like WRF) or isentropic approaches
2451  if (solverChoice.sounding_type == SoundingType::Ideal ||
2452  solverChoice.sounding_type == SoundingType::Isentropic ||
2453  solverChoice.sounding_type == SoundingType::DryIsentropic) {
2455  "Gravity should be on to be consistent with sounding initialization.");
2456  } else { // SoundingType::ConstantDensity
2458  "Constant density probably doesn't make sense for compressible flow with gravity");
2459  initHSE();
2460  }
2461 
2462 #ifdef ERF_USE_NETCDF
2463  }
2464  else if (solverChoice.init_type == InitType::WRFInput && !nc_init_file[lev].empty())
2465  {
2466  // The base state is initialized from WRF wrfinput data, output by
2467  // ideal.exe or real.exe
2468 
2469  init_from_wrfinput(lev, *mf_PSFC[lev]);
2470 
2471  // The physbc's need the terrain but are needed for initHSE
2472  make_physbcs(lev);
2473  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
2474  }
2475  else if (solverChoice.init_type == InitType::WRFInput && nc_init_file[lev].empty())
2476  {
2477  //
2478  // A refined level with no wrfinput file of its own: the level was created by
2479  // ERF's own refinement machinery rather than read from a wrfinput_d0*. Level 0
2480  // must always have a file (checked in ParameterSanityChecks), so this is lev > 0.
2481  //
2482  // Note the caller has already built the terrain at this level for us -- see the
2483  // ordering in MakeNewLevelFromScratch -- because unlike the has-a-file case there
2484  // is no file here to build z_phys from, and the base state below needs it.
2485  //
2486  AMREX_ALWAYS_ASSERT(lev > 0);
2487  AMREX_ALWAYS_ASSERT(z_phys_cc[lev] != nullptr);
2488 
2489  make_physbcs(lev);
2490 
2491  // Fill the part of this level that lies inside the domain but outside the fine
2492  // grids, and the fine ghost cells, from the coarser level.
2494 
2495  // Now build the base state on the fine grids by exactly the construction level 0
2496  // used: the analytic reference profile from the level-0 parameters, evaluated at
2497  // this level's cell-centered heights, followed by the discrete hydrostatic
2498  // rebalance. Running the same procedure on both levels is what makes their base
2499  // states agree; interpolating the coarse base state instead would leave this level
2500  // not discretely hydrostatic on its own mesh.
2501  rebuild_base_state_from_wrfinput(lev, base_state[lev]);
2502  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
2503 
2504  // The state itself is interpolated from the coarser level. This must come after the
2505  // base state, since it interpolates perturbational quantities relative to it.
2506  FillCoarsePatch(lev, elapsed_time);
2507 
2508  // The 2D/surface arrays likewise have no file to come from at this level. That
2509  // includes PSFC and the land mask / land type / soil type, which Interp2DArrays
2510  // takes from the parent for a level with no init file of its own.
2511  Interp2DArrays(lev, ba2d[lev], dmap[lev]);
2512  }
2513  else if (solverChoice.init_type == InitType::NCFile)
2514  {
2515  // The state is initialized by reading from a Netcdf file
2516  init_from_ncfile(lev);
2517 
2518  // The physbc's need the terrain but are needed for initHSE
2519  make_physbcs(lev);
2520  }
2521  else if (solverChoice.init_type == InitType::Metgrid)
2522  {
2523  // The base state is initialized from data output by WPS metgrid;
2524  // we will rebalance after interpolation
2525  init_from_metgrid(lev);
2526 #endif
2527  } else if ( (solverChoice.init_type == InitType::Uniform ) ||
2528  (solverChoice.init_type == InitType::ConstantDensity) ||
2529  (solverChoice.init_type == InitType::Isentropic ) ||
2530  (solverChoice.init_type == InitType::ConstantDensityLinearTheta ) ||
2531  (solverChoice.init_type == InitType::HindCast ) ||
2532  (solverChoice.init_type == InitType::MoistBaseState ) ) {
2533  // Initialize a uniform density/entropy background field and base state
2534  // based on the problem-specified reference density and temperature
2535 
2536  // The physbc's need the terrain but are needed for initHSE
2537  make_physbcs(lev);
2538 
2539  // We will initialize the state from the background state so must set that first
2540  // The choice between constant rho and constant theta will be made inside initHSE
2541  initHSE(lev);
2542 
2543  // Copy rho and rhotheta from rho_hse and p_hse
2544  init_from_hse(lev);
2545 
2546  if (lev == 0 && cloud_chamber_config.active &&
2548  MultiFab p0(base_state[lev], make_alias, BaseState::p0_comp, 1);
2549  const Real p0_min = p0.min(0);
2550  const Real p0_max = p0.max(0);
2551  if (ParallelDescriptor::IOProcessor()) {
2552  Print() << "Cloud Chamber base pressure [Pa]: min=" << p0_min
2553  << " max=" << p0_max
2554  << " lower-boundary=" << p0_max << "\n";
2555  }
2556  }
2557 
2558  } else {
2559  Abort("Unknown init_type!");
2560  }
2561 
2562  // Add problem-specific flow features
2563  //
2564  // Notes:
2565  // - This calls init_custom_pert that is defined for each problem
2566  // - This may modify the base state
2567  // - The fields set by init_custom_pert are **perturbations** to the
2568  // background flow set based on init_type
2569  if (solverChoice.init_type != InitType::NCFile) {
2570  init_custom(lev);
2571  }
2572 
2573  // Ensure that the face-based data are the same on both sides of a periodic domain.
2574  // The data associated with the lower grid ID is considered the correct value.
2575  lev_new[Vars::xvel].OverrideSync(geom[lev].periodicity());
2576  lev_new[Vars::yvel].OverrideSync(geom[lev].periodicity());
2577  lev_new[Vars::zvel].OverrideSync(geom[lev].periodicity());
2578 
2579  if (solverChoice.spongeChoice.sponge_type == SpongeType::Input_Sponge)
2580  {
2581  input_sponge(lev);
2582  }
2583 
2584  // Initialize turbulent perturbation
2585  if (solverChoice.use_perturbation(lev)) {
2586  turbPert_update(lev, zero);
2588  turbPert_amplitude_w(lev);
2589  } else {
2590  turbPert_amplitude(lev);
2591  }
2592  }
2593 
2594  // Set initial velocity field for immersed cells to be close to 0
2595  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
2596  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
2597  init_immersed_forcing(lev);
2598  }
2599 }
void turbPert_amplitude_w(const int lev)
Definition: ERF_InitTurbPert.cpp:72
void init_from_input_sounding(int lev)
Definition: ERF_InitFromInputSounding.cpp:95
void turbPert_update(const int lev, const double dt)
Definition: ERF_InitTurbPert.cpp:18
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:208
void input_sponge(int lev)
Definition: ERF_InitSponge.cpp:17
void Interp2DArrays(int lev, const amrex::BoxArray &my_ba2d, const amrex::DistributionMapping &my_dm)
Definition: ERF.cpp:2068
void make_physbcs(int lev)
Definition: ERF_MakeNewArrays.cpp:1305
void init_immersed_forcing(int lev)
Definition: ERF_InitImmersedForcing.cpp:15
void interp_base_state_from_coarse(int lev)
Definition: ERF_Init1D.cpp:179
void turbPert_amplitude(const int lev)
Definition: ERF_InitTurbPert.cpp:42
bool use_gravity
Whether gravitational forcing is enabled.
Definition: ERF_DataStruct.H:2025
SpongeChoice spongeChoice
Sponge-layer options.
Definition: ERF_DataStruct.H:1973
Here is the call graph for this function:

◆ init_phys_bcs()

void ERF::init_phys_bcs ( 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.

Parameters
[in,out]read_prim_thetaWhether external Dirichlet theta values should be read as primitive theta.
27 {
28  bool anelastic = false;
29  for (int lev = 0; lev <= max_level; ++lev) {
30  anelastic = anelastic || (solverChoice.anelastic[lev] != 0);
31  }
32 
33  auto f = [this,&read_prim_theta,anelastic,one_d=one,zero_d=zero] (std::string const& bcid, Orientation ori)
34  {
35  // These are simply defaults for Dirichlet faces -- they should be over-written below
37  m_bc_extdir_vals[BCVars::RhoTheta_bc_comp][ori] = -one_d; // It is important to set this negative
38  // because the sign is tested on below
39  for (int n = BCVars::RhoKE_bc_comp; n < BCVars::xvel_bc; n++) {
40  m_bc_extdir_vals[n][ori] = zero_d;
41  }
42 
43  m_bc_extdir_vals[BCVars::xvel_bc][ori] = zero_d; // default
44  m_bc_extdir_vals[BCVars::yvel_bc][ori] = zero_d;
45  m_bc_extdir_vals[BCVars::zvel_bc][ori] = zero_d;
46 
47  // These are simply defaults for Neumann gradients -- they should be over-written below
50 
59 
60  m_bc_neumann_vals[BCVars::xvel_bc][ori] = zero_d;
61  m_bc_neumann_vals[BCVars::yvel_bc][ori] = zero_d;
62  m_bc_neumann_vals[BCVars::zvel_bc][ori] = zero_d;
63 
64  for (int n = 0; n < NBCVAR_max; ++n) {
66  }
67 
68  std::string pp_text = pp_prefix + "." + bcid;
69  ParmParse pp(pp_text);
70 
71  std::string bc_type_in;
72  if (pp.queryAdd("type", bc_type_in) <= 0)
73  {
74  pp_text = bcid;
75  pp = ParmParse(pp_text);
76  pp.queryAdd("type", bc_type_in);
77  }
78 
79  std::string bc_type = amrex::toLower(bc_type_in);
80 
81  auto apply_wall_scalar = [this,ori] (int comp,
83  {
85  if (parsed.intent == Intent::DirichletConserved ||
86  parsed.intent == Intent::DirichletPrimitive) {
87  m_bc_extdir_vals[comp][ori] = parsed.stored_value;
88  } else if (parsed.intent == Intent::Neumann) {
89  m_bc_neumann_vals[comp][ori] = parsed.stored_value;
90  }
91  m_wall_scalar_bc_intent[comp][ori] = parsed.intent;
92  };
93 
94  if (bc_type == "symmetry")
95  {
96  // Print() << bcid << " set to symmetry.\n";
98  domain_bc_type[ori] = "Symmetry";
99  }
100  else if ( (ori.coordDir() != 2 && solverChoice.use_real_bcs) || (bc_type == "outflow") )
101  {
102  // Print() << bcid << " set to outflow.\n";
104  domain_bc_type[ori] = "Outflow";
105  }
106  else if (bc_type == "open")
107  {
108  // Print() << bcid << " set to open.\n";
109  AMREX_ASSERT_WITH_MESSAGE((ori.coordDir() != 2), "Open boundary not valid on zlo or zhi!");
110  phys_bc_type[ori] = ERF_BC::open;
111  domain_bc_type[ori] = "Open";
112  }
113  else if (bc_type == "ho_outflow")
114  {
116  domain_bc_type[ori] = "HO_Outflow";
117  }
118 
119  else if (bc_type == "inflow" || bc_type == "inflow_outflow")
120  {
121  if (bc_type == "inflow") {
122  // Print() << bcid << " set to inflow.\n";
124  domain_bc_type[ori] = "Inflow";
125  } else {
126  // Print() << bcid << " set to inflow_outflow.\n";
128  domain_bc_type[ori] = "InflowOutflow";
129  }
130 
131  std::vector<Real> v;
132  if (input_bndry_planes && m_r2d->ingested_velocity()) {
133  m_bc_extdir_vals[BCVars::xvel_bc][ori] = zero_d;
134  m_bc_extdir_vals[BCVars::yvel_bc][ori] = zero_d;
135  m_bc_extdir_vals[BCVars::zvel_bc][ori] = zero_d;
136  } else {
137  // Test for input data file if at xlo face
138  std::string dirichlet_file;
139  auto file_exists = pp.queryAdd("dirichlet_file", dirichlet_file);
140  if (file_exists) {
141  pp.queryAdd("read_prim_theta", read_prim_theta);
142  init_Dirichlet_bc_data(dirichlet_file);
143  } else {
144  pp.getarr("velocity", v, 0, AMREX_SPACEDIM);
145  m_bc_extdir_vals[BCVars::xvel_bc][ori] = v[0];
146  m_bc_extdir_vals[BCVars::yvel_bc][ori] = v[1];
147  m_bc_extdir_vals[BCVars::zvel_bc][ori] = v[2];
148  }
149  }
150 
151  Real rho_in = zero_d;
152  if (input_bndry_planes && m_r2d->ingested_density()) {
153  m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] = zero_d;
154  } else {
155  // Negative sentinel rather than the queryAdd return value: a density is
156  // always positive -- the "rho_in > 0" test just below already reads
157  // presence off the value -- whereas the return value only reports whether
158  // the key existed before this call. init_bcs() runs twice on restart, so
159  // the second pass would otherwise see every key as user-specified.
160  rho_in = Real(-1.0);
161  pp.queryAdd("density", rho_in);
162  if (rho_in <= zero_d) {
163  rho_in = zero_d;
164  amrex::Print() << "Using interior values to set conserved vars" << std::endl;
165  }
166  m_bc_extdir_vals[BCVars::Rho_bc_comp][ori] = rho_in;
167  }
168 
169  bool th_read = (th_bc_data[0].data()!=nullptr);
170  Real theta_in = zero_d;
171  if (input_bndry_planes && m_r2d->ingested_theta()) {
173  } else if (!th_read) {
174  if (rho_in > 0) {
175  pp.get("theta", theta_in);
176  }
177  m_bc_extdir_vals[BCVars::RhoTheta_bc_comp][ori] = rho_in*theta_in;
178  }
179 
180  // Non-reflecting inflow: prescribe velocity and density but
181  // extrapolate RhoTheta (and hence pressure) from the interior.
182  // This lets upstream-propagating acoustic waves exit the domain
183  // instead of reflecting off the rigid Dirichlet boundary.
184  bool nonreflecting = false;
185  pp.queryAdd("nonreflecting", nonreflecting);
186  m_bc_nonreflecting[ori] = nonreflecting;
187 
188  Real scalar_in = zero_d;
189  if (input_bndry_planes && m_r2d->ingested_scalar()) {
191  } else {
192  if (pp.query("scalar", scalar_in))
193  m_bc_extdir_vals[BCVars::RhoScalar_bc_comp][ori] = rho_in*scalar_in;
194  }
195 
196  if (solverChoice.moisture_type != MoistureType::None) {
197  Real qv_in = Real(-1.0);
198  if (input_bndry_planes && m_r2d->ingested_q1()) {
200  } else {
201  // Negative sentinel rather than the queryAdd return value: a mixing
202  // ratio is never negative, and the return value only reports whether
203  // the key existed before this call (init_bcs() runs twice on restart).
204  pp.queryAdd("qv", qv_in);
205  if (qv_in >= zero_d) {
206  m_bc_extdir_vals[BCVars::RhoQ1_bc_comp][ori] = rho_in*qv_in;
207  }
208  }
209  Real qc_in = Real(-1.0);
210  if (input_bndry_planes && m_r2d->ingested_q2()) {
212  } else {
213  // Negative sentinel rather than the queryAdd return value: a mixing
214  // ratio is never negative, and the return value only reports whether
215  // the key existed before this call (init_bcs() runs twice on restart).
216  pp.queryAdd("qc", qc_in);
217  if (qc_in >= zero_d) {
218  m_bc_extdir_vals[BCVars::RhoQ2_bc_comp][ori] = rho_in*qc_in;
219  }
220  }
221  }
222 
223  Real KE_in = Real(-1.0);
224  if (input_bndry_planes && m_r2d->ingested_KE()) {
226  } else {
227  // Negative sentinel rather than the queryAdd return value: a turbulent
228  // kinetic energy is never negative, and the return value only reports
229  // whether the key existed before this call (init_bcs() runs twice on
230  // restart).
231  pp.queryAdd("KE", KE_in);
232  if (KE_in >= zero_d) {
233  m_bc_extdir_vals[BCVars::RhoKE_bc_comp][ori] = rho_in*KE_in;
234  }
235  }
236  }
237  else if (bc_type == "noslipwall")
238  {
239  // Print() << bcid <<" set to no-slip wall.\n";
241  domain_bc_type[ori] = "NoSlipWall";
242 
243  std::vector<Real> v;
244 
245  // The values of m_bc_extdir_vals default to zero
246  // But if we find "velocity" in the inputs file, use those values instead.
247  if (pp.queryarr("velocity", v, 0, AMREX_SPACEDIM))
248  {
249  v[ori.coordDir()] = zero_d;
250  m_bc_extdir_vals[BCVars::xvel_bc][ori] = v[0];
251  m_bc_extdir_vals[BCVars::yvel_bc][ori] = v[1];
252  m_bc_extdir_vals[BCVars::zvel_bc][ori] = v[2];
253  }
254 
257  if (!parsed.ok()) {
258  amrex::Error(parsed.error);
259  }
260  apply_wall_scalar(BCVars::Rho_bc_comp, parsed.scalars.density);
261  apply_wall_scalar(BCVars::RhoTheta_bc_comp, parsed.scalars.theta);
262  apply_wall_scalar(BCVars::RhoQ1_bc_comp, parsed.scalars.qv);
263  }
264  else if (bc_type == "slipwall")
265  {
266  // Print() << bcid <<" set to slip wall.\n";
267 
269  domain_bc_type[ori] = "SlipWall";
270 
272  pp, bcid, erf_wall_scalar_bc::SolidWallKind::Slip, anelastic);
273  if (!parsed.ok()) {
274  amrex::Error(parsed.error);
275  }
276  apply_wall_scalar(BCVars::Rho_bc_comp, parsed.scalars.density);
277  apply_wall_scalar(BCVars::RhoTheta_bc_comp, parsed.scalars.theta);
278  }
279  else if (bc_type == "surface_layer")
280  {
282  domain_bc_type[ori] = "surface_layer";
283  }
284  else
285  {
287  }
288 
289  if (geom[0].isPeriodic(ori.coordDir())) {
290  domain_bc_type[ori] = "Periodic";
291  if (phys_bc_type[ori] == ERF_BC::undefined)
292  {
294  } else {
295  Abort("Wrong BC type for periodic boundary");
296  }
297  }
298 
299  if (phys_bc_type[ori] == ERF_BC::undefined)
300  {
301  if (solverChoice.use_real_bcs && ori.coordDir() < 2) {
302  Print() << "We are using real bc's so don't need to set lateral bc's" << std::endl;
303  } else {
304  Print() << "BC Type specified for face " << bcid << " is " << bc_type_in << std::endl;
305  Abort("This BC type is unknown");
306  }
307  }
308  };
309 
310  f("xlo", Orientation(Direction::x,Orientation::low));
311  f("xhi", Orientation(Direction::x,Orientation::high));
312  f("ylo", Orientation(Direction::y,Orientation::low));
313  f("yhi", Orientation(Direction::y,Orientation::high));
314  f("zlo", Orientation(Direction::z,Orientation::low));
315  f("zhi", Orientation(Direction::z,Orientation::high));
316 }
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:796
amrex::Array< amrex::Array< amrex::Real, AMREX_SPACEDIM *2 >, AMREX_SPACEDIM+NBCVAR_max > m_bc_neumann_vals
Definition: ERF.H:1439
@ RhoQ6_bc_comp
Definition: ERF_IndexDefines.H:99
@ RhoQ4_bc_comp
Definition: ERF_IndexDefines.H:97
@ RhoQ3_bc_comp
Definition: ERF_IndexDefines.H:96
@ RhoQ5_bc_comp
Definition: ERF_IndexDefines.H:98
WallScalarBCIntent
Definition: ERF_WallScalarBC.H:13
WallFaceParseResult parse_wall_face_scalars(amrex::ParmParse &pp, const std::string &face, SolidWallKind wall_kind, bool anelastic)
Definition: ERF_WallScalarBC.H:165
Definition: ERF_WallScalarBC.H:25
Here is the call graph for this function:

◆ init_stations()

void ERF::init_stations ( )
685 {
686  if (!station_sampler) { return; }
687 
688  auto& stations = station_sampler->stations();
689 
690  // The set of 2D diagnostics this configuration can actually fill, selected
691  // exactly as Write2DPlotFile selects them.
692  const bool has_surface_layer =
693  phys_bc_type[Orientation(Direction::z, Orientation::low)] == ERF_BC::surface_layer;
694  const auto active_lsm_names = lsm.Get_DataNames();
695  const auto available_2d = plotfile2d::available_diagnostic_names(solverChoice,
696  has_surface_layer,
697  active_lsm_names);
698 
699  Vector<std::string> requested_3d;
700  Vector<std::string> requested_2d;
701 
702  for (auto& station : stations)
703  {
704  for (auto& var : station.vars)
705  {
706  // Is it a 3D plot variable that this configuration can produce?
707  Vector<std::string> probe{var.name};
709  appendPlot3DVariables(Vector<std::string>{var.name}, probe);
710  if (containerHasElement(probe, var.name)) {
711  var.is_2d = false;
712  if (!containerHasElement(requested_3d, var.name)) {
713  requested_3d.push_back(var.name);
714  }
715  continue;
716  }
717 
718  // Is it a 2D diagnostic?
719  const auto* descriptor = plotfile2d::find_diagnostic(var.name);
720  if (descriptor == nullptr) {
722  }
723  if (descriptor != nullptr) {
724  var.is_2d = true;
725  var.units = descriptor->units;
726  if (containerHasElement(available_2d, var.name)) {
727  if (!containerHasElement(requested_2d, var.name)) {
728  requested_2d.push_back(var.name);
729  }
730  } else {
731  // Same convention as the 2D plotfile: a diagnostic that this
732  // run does not compute is written as its documented missing
733  // value rather than dropped.
734  var.is_missing = true;
735  var.missing_value =
737  ? Real(0.0) : Real(-999.0);
738  }
739  continue;
740  }
741 
742  // Neither: say which of the two failures it is.
743  if (containerHasElement(derived_names, var.name) ||
744  containerHasElement(cons_names, var.name) ||
745  var.name == "x_velocity" || var.name == "y_velocity" || var.name == "z_velocity")
746  {
747  Abort("Station '" + station.name + "': '" + var.name +
748  "' is a 3D plotfile variable but is not available in this configuration");
749  }
750  Abort("Station '" + station.name + "': '" + var.name +
751  "' is neither a 3D plotfile variable nor a built-in 2D diagnostic, which "
752  "are the two catalogs a station can draw on. Note that a 2D plotfile can "
753  "also carry sampled-level fields, named for the field and the level such "
754  "as 'theta_z100m', and those are not among them: ask for the 3D variable "
755  "itself and give the station a height instead. See erf.plot_vars_1 and "
756  "erf.plot2d_vars_1 in the documentation for the names that can be requested");
757  }
758  }
759 
760  // The fill lists, in the order the fill routines produce their components.
761  station_vars_3d = requested_3d;
763  appendPlot3DVariables(requested_3d, station_vars_3d);
764 
766 
767  for (auto& station : stations) {
768  for (auto& var : station.vars) {
769  if (var.is_missing) { continue; }
770  const auto& list = var.is_2d ? station_vars_2d : station_vars_3d;
771  for (int i = 0; i < static_cast<int>(list.size()); ++i) {
772  if (list[i] == var.name) { var.comp = i; break; }
773  }
774  AMREX_ALWAYS_ASSERT(var.comp >= 0);
775  }
776 
777  if (station.has_3d_vars() && station.heights.empty()) {
778  Abort("Station '" + station.name + "': erf." + station.name + ".height_agl or erf." +
779  station.name + ".height_abs must be given, since this station requests a 3D "
780  "variable");
781  }
782  }
783 
785 
786  station_sampler->buildColumns();
787  station_sampler->setWriteTimestamp(use_datetime, datetime_format);
788 
789  // Everything the signature describes is now resolved, so a restart that
790  // would append to a file describing different columns stops here, before the
791  // run does any work.
792  station_sampler->checkRestartFiles();
793 
794  if (verbose > 0) {
795  Print() << "Station output: " << stations.size() << " station(s), "
796  << station_sampler->numColumns() << " column(s) per output step" << std::endl;
797  }
798 }
amrex::Vector< std::string > station_vars_3d
Definition: ERF.H:2130
void canonicalizePlot3DVariables(amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile.cpp:57
void resolve_station_positions()
Definition: ERF_StationSampler.cpp:806
amrex::Vector< std::string > station_vars_2d
Definition: ERF.H:2131
amrex::Vector< std::string > Get_DataNames()
Definition: ERF_LandSurface.H:137
PlotVariableSelection select_requested_plot_variables(const amrex::Vector< std::string > &requested, const amrex::Vector< std::string > &available)
Definition: ERF_Plotfile2DUtils.cpp:34
amrex::Vector< std::string > available_diagnostic_names(const SolverChoice &solver_choice)
Definition: ERF_Plotfile2DWaterPath.cpp:143
amrex::Vector< std::string > accepted
Definition: ERF_Plotfile2DUtils.H:34
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
29 {
30  // ********************************************************************************************
31  // Base state holds r_0, pres_0, pi_0, th_0 (in that order)
32  //
33  // Here is where we set the number of ghost cells for the base state!
34  // ********************************************************************************************
35  int ngb = ComputeGhostCells(solverChoice) + 1;
36  tmp_base_state.define(ba,dm,BaseState::num_comps,ngb);
37  tmp_base_state.setVal(zero);
38 
39  if (solverChoice.terrain_type == TerrainType::MovingFittedMesh) {
40  base_state_new[lev].define(ba,dm,BaseState::num_comps,base_state[lev].nGrowVect());
41  base_state_new[lev].setVal(zero);
42  }
43 
44  // ********************************************************************************************
45  // Allocate terrain arrays
46  // ********************************************************************************************
47 
48  BoxArray ba_nd(ba);
49  ba_nd.surroundingNodes();
50 
51  // NOTE: this is where we actually allocate z_phys_nd -- but here it's called "tmp_zphys_nd"
52  // We need this to be one greater than the ghost cells to handle levels > 0
53 
54  int ngrow = ComputeGhostCells(solverChoice) + 2;
55  tmp_zphys_nd = std::make_unique<MultiFab>(ba_nd,dm,1,IntVect(ngrow,ngrow,ngrow));
56 
57  // Offset z-coordinate for interpolate_1d when plane EB is used.
58  Real z_offset = zero;
59  if (solverChoice.terrain_type == TerrainType::EB) {
60  ParmParse pp_eb2("eb2");
61  std::string geometry;
62  pp_eb2.queryAdd("geometry", geometry);
63  if (geometry == "plane") {
64  RealArray plane_point{zero, zero, zero};
65  pp_eb2.queryAdd("plane_point", plane_point);
66  z_offset = plane_point[2];
67  }
68  }
69 
70  z_phys_cc[lev] = std::make_unique<MultiFab>(ba,dm,1,2);
71  init_default_zphys(lev, geom[lev], *tmp_zphys_nd, *z_phys_cc[lev], z_offset);
72 
73  if (solverChoice.terrain_type == TerrainType::MovingFittedMesh)
74  {
75  detJ_cc_new[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
76  detJ_cc_src[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
77 
78  ax_src[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(1,0,0)),dm,1,1);
79  ay_src[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,1,0)),dm,1,1);
80  az_src[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)),dm,1,1);
81 
82  z_t_rk[lev] = std::make_unique<MultiFab>( convert(ba, IntVect(0,0,1)), dm, 1, 1 );
83 
84  z_phys_nd_new[lev] = std::make_unique<MultiFab>(ba_nd,dm,1,IntVect(ngrow,ngrow,ngrow));
85  z_phys_nd_src[lev] = std::make_unique<MultiFab>(ba_nd,dm,1,IntVect(ngrow,ngrow,ngrow));
86  z_phys_cc_src[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
87  }
88  else
89  {
90  z_phys_nd_new[lev] = nullptr;
91  detJ_cc_new[lev] = nullptr;
92 
93  z_phys_nd_src[lev] = nullptr;
94  z_phys_cc_src[lev] = nullptr;
95  detJ_cc_src[lev] = nullptr;
96 
97  z_t_rk[lev] = nullptr;
98  }
99 
100  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
101  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
102  {
103  terrain_blanking[lev] = std::make_unique<MultiFab>(ba,dm,1,ngrow);
104  terrain_blanking[lev]->setVal(one);
105 
106 #if USE_FC_FACTORY
107  // Face-centered terrain blanking for momentum forcing
108  terrain_blanking_xface[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(1,0,0)),dm,1,ngrow);
109  terrain_blanking_yface[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,1,0)),dm,1,ngrow);
110  terrain_blanking_zface[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)),dm,1,ngrow);
111  terrain_blanking_xface[lev]->setVal(one);
112  terrain_blanking_yface[lev]->setVal(one);
113  terrain_blanking_zface[lev]->setVal(one);
114 #endif
115 
116  // Initialize planar average storage for immersed forcing
117  // Sized to match PlaneAverage output with ghost cells, using same indexing as subsidence
118  Box domain = geom[lev].Domain();
119  Box tdomain = domain; tdomain.grow(2, 1); // Grow by 1 ghost cell in z-direction
120  r_plane_avg[lev].resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
121  t_plane_avg[lev].resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
122  }
123 
124  // We use these area arrays regardless of terrain, EB or none of the above
125  detJ_cc[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
126  ax[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(1,0,0)),dm,1,1);
127  ay[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,1,0)),dm,1,1);
128  az[lev] = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)),dm,1,1);
129 
130  detJ_cc[lev]->setVal(one);
131  ax[lev]->setVal(one);
132  ay[lev]->setVal(one);
133  az[lev]->setVal(one);
134 
135  // ********************************************************************************************
136  // Create wall distance array for RANS modeling
137  // ********************************************************************************************
138  if (solverChoice.turbChoice[lev].rans_type != RANSType::None) {
139  walldist[lev] = std::make_unique<MultiFab>(ba,dm,1,1);
140  walldist[lev]->setVal(bogus_large_value);
141  } else {
142  walldist[lev] = nullptr;
143  }
144 
145  // ********************************************************************************************
146  // These are the persistent containers for the old and new data
147  // ********************************************************************************************
148  int ncomp;
149  if (lev > 0) {
150  ncomp = vars_new[lev-1][Vars::cons].nComp();
151  } else {
152  int n_qstate = micro->Get_Qstate_Size();
153  ncomp = NDRY + NSCALARS + n_qstate;
154  }
155 
156  // ********************************************************************************************
157  // The number of ghost cells for density must be 1 greater than that for velocity
158  // so that we can go back in forth between velocity and momentum on all faces
159  // ********************************************************************************************
160  int ngrow_state = ComputeGhostCells(solverChoice) + 1;
161  int ngrow_vels = ComputeGhostCells(solverChoice);
162 
163  // ********************************************************************************************
164  // New solution data containers
165  // ********************************************************************************************
166  if (solverChoice.terrain_type != TerrainType::EB) {
167  lev_new[Vars::cons].define(ba, dm, ncomp, ngrow_state);
168  lev_old[Vars::cons].define(ba, dm, ncomp, ngrow_state);
169  } else {
170  // EB: Define the MultiFabs with the EBFactory
171  lev_new[Vars::cons].define(ba, dm, ncomp, ngrow_state, MFInfo(), EBFactory(lev));
172  lev_old[Vars::cons].define(ba, dm, ncomp, ngrow_state, MFInfo(), EBFactory(lev));
173  }
174 
175  // Initialize all components to zero so we don't need to explicitly set
176  // scalars / moisture variables to zero in the initialization
177  lev_new[Vars::cons].setVal(zero);
178  lev_old[Vars::cons].setVal(zero);
179 
180  lev_new[Vars::xvel].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
181  lev_old[Vars::xvel].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
182 
183  lev_new[Vars::yvel].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
184  lev_old[Vars::yvel].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
185 
186  // Set these to avoid operations on uninitialized data
187  lev_new[Vars::xvel].setVal(bogus_large_value);
188  lev_old[Vars::xvel].setVal(bogus_large_value);
189  lev_new[Vars::yvel].setVal(bogus_large_value);
190  lev_old[Vars::yvel].setVal(bogus_large_value);
191 
192  // Note that we need the ghost cells in the z-direction if we are doing any
193  // kind of domain decomposition in the vertical (at level 0 or above)
194  lev_new[Vars::zvel].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
195  lev_old[Vars::zvel].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
196 
197  gradp[lev][GpVars::gpx].define(convert(ba, IntVect(1,0,0)), dm, 1, 1); gradp[lev][GpVars::gpx].setVal(zero);
198  gradp[lev][GpVars::gpy].define(convert(ba, IntVect(0,1,0)), dm, 1, 1); gradp[lev][GpVars::gpy].setVal(zero);
199  gradp[lev][GpVars::gpz].define(convert(ba, IntVect(0,0,1)), dm, 1, 1); gradp[lev][GpVars::gpz].setVal(zero);
200 
201  if ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) {
202  pp_inc[lev].define(ba, dm, 1, 1);
203  pp_inc[lev].setVal(zero);
204  }
205 
206  // We use this in the fast substepping only
207  if (solverChoice.anelastic[lev] == 0) {
208  lagged_delta_rt[lev].define(ba, dm, 1, 1);
209  lagged_delta_rt[lev].setVal(zero);
210  }
211 
212  // We use these for advecting the slow variables, whether anelastic or compressible
213  avg_xmom[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, 1);
214  avg_ymom[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, 1);
215  avg_zmom[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, 1);
216  avg_xmom[lev].setVal(0); avg_ymom[lev].setVal(0); avg_zmom[lev].setVal(zero);
217 
218  // ********************************************************************************************
219  // These are just used for scratch in the time integrator but we might as well define them here
220  // ********************************************************************************************
221  if (solverChoice.terrain_type != TerrainType::EB) {
222  rU_old[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
223  rU_new[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels);
224 
225  rV_old[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
226  rV_new[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels);
227 
228  rW_old[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
229  rW_new[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels);
230  } else {
231  // EB: Define the MultiFabs with the EBFactory
232  rU_old[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
233  rU_new[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
234 
235  rV_old[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
236  rV_new[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
237 
238  rW_old[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
239  rW_new[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, ngrow_vels, MFInfo(), EBFactory(lev));
240  }
241 
242  if (lev > 0) {
243  //xmom_crse_rhs[lev].define(convert(ba, IntVect(1,0,0)), dm, 1, IntVect{0});
244  //ymom_crse_rhs[lev].define(convert(ba, IntVect(0,1,0)), dm, 1, IntVect{0});
245  zmom_crse_rhs[lev].define(convert(ba, IntVect(0,0,1)), dm, 1, IntVect{0});
246  }
247 
248  // We do this here just so they won't be undefined in the initial FillPatch
249  rU_old[lev].setVal(bogus_large_value);
250  rV_old[lev].setVal(bogus_large_value);
251  rW_old[lev].setVal(bogus_large_value);
252  rU_new[lev].setVal(bogus_large_value);
253  rV_new[lev].setVal(bogus_large_value);
254  rW_new[lev].setVal(bogus_large_value);
255 
256  // ********************************************************************************************
257  // These are just time averaged fields for diagnostics
258  // ********************************************************************************************
259 
260  // NOTE: We are not completing a fillpatch call on the time averaged data;
261  // which would copy on intersection and interpolate from coarse.
262  // Therefore, we are restarting the averaging when the ba changes,
263  // this may give poor statistics for dynamic mesh refinement.
264  vel_t_avg[lev] = nullptr;
266  vel_t_avg[lev] = std::make_unique<MultiFab>(ba, dm, 4, 0); // Each vel comp and the mag
267  vel_t_avg[lev]->setVal(zero);
268  t_avg_cnt[lev] = zero;
269  }
270 
271  // Components: u, v, w, theta, uu, vv, ww, uw, vw, wtheta.
272  // The field storage is rebuilt for this level, but the time-reset state is
273  // global and must survive regridding of any level.
274  interval_means[lev] = nullptr;
276  interval_means[lev] = std::make_unique<MultiFab>(ba, dm, 10, 0);
277  interval_means[lev]->setVal(zero);
278  t_mean_cnt[lev] = 0.0;
279  }
280 
281  // ********************************************************************************************
282  // Initialize flux registers whenever we create/re-create a level
283  // ********************************************************************************************
284  if (solverChoice.coupling_type == CouplingType::TwoWay) {
285  if (lev == 0) {
286  advflux_reg[0] = nullptr;
287  } else {
288  int ncomp_reflux = vars_new[0][Vars::cons].nComp();
289  advflux_reg[lev] = new YAFluxRegister(ba , grids[lev-1],
290  dm , dmap[lev-1],
291  geom[lev], geom[lev-1],
292  ref_ratio[lev-1], lev, ncomp_reflux);
293  }
294  }
295 
296  // ********************************************************************************************
297  // Define Theta_prim storage if using surface_layer BC
298  // ********************************************************************************************
299  Theta_prim[lev] = nullptr;
300  Qv_prim[lev] = nullptr;
301  Qr_prim[lev] = nullptr;
302 
303  for (OrientationIter oit; oit; ++oit) {
304  Orientation ori = oit();
305  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
306  amrex::Print() << " Found MOST at face " << ori << " : Constructing primitive vars for MOST.." << std::endl;
307  Theta_prim[lev] = std::make_unique<MultiFab>(ba,dm,1,IntVect(ngrow_state,ngrow_state,1));
308  if (solverChoice.moisture_type != MoistureType::None) {
309  Qv_prim[lev] = std::make_unique<MultiFab>(ba,dm,1,IntVect(ngrow_state,ngrow_state,1));
310  Qr_prim[lev] = std::make_unique<MultiFab>(ba,dm,1,IntVect(ngrow_state,ngrow_state,1));
311  } else {
312  Qv_prim[lev] = nullptr;
313  Qr_prim[lev] = nullptr;
314  }
315  // these only need to be defined once
316  break;
317  }
318  }
319 
320  // ********************************************************************************************
321  // Build 1D BA and 2D BA
322  // ********************************************************************************************
323 
324  // NOTE: By design, the compressed BAs have their compressed indices set to 0
325  // MFIters that need more detailed box information should be done over 3D MFs
326 
327  // Build 2D BA
328  BoxList bl2d = ba.boxList();
329  for (auto& b : bl2d) {
330  b.setRange(2,0);
331  }
332  ba2d[lev] = BoxArray(std::move(bl2d));
333 
334  // Build 1D BA
335  BoxList bl1d = ba.boxList();
336  for (auto& b : bl1d) {
337  b.setRange(0,0);
338  b.setRange(1,0);
339  }
340  ba1d[lev] = BoxArray(std::move(bl1d));
341 
342  // ********************************************************************************************
343  // Vertical extent of the grid column over each (i,j) -- needed by the implicit
344  // vertical diffusion solves, which must treat a column as one tridiagonal system
345  // ********************************************************************************************
346  define_column_kextent(lev, ba, dm);
347 
348  // ********************************************************************************************
349  // Map factors
350  // ********************************************************************************************
351  mapfac[lev].resize(MapFacType::num);
352  mapfac[lev][MapFacType::m_x] = std::make_unique<MultiFab>( ba2d[lev],dm,1,IntVect(3,3,0));
353  mapfac[lev][MapFacType::u_x] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(1,0,0)),dm,1,IntVect(3,3,0));
354  mapfac[lev][MapFacType::v_x] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(0,1,0)),dm,1,IntVect(3,3,0));
355 
356 #if 0
357  // For now we comment this out to avoid CI failures but we will need to re-enable
358  // this if using non-conformal mappings
360  mapfac[lev][MapFacType::m_y] = std::make_unique<MultiFab>(ba2d[lev],dm,1,IntVect(3,3,0));
361  }
363  mapfac[lev][MapFacType::u_y] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(1,0,0)),dm,1,IntVect(3,3,0));
364  }
366  mapfac[lev][MapFacType::v_y] = std::make_unique<MultiFab>(convert(ba2d[lev],IntVect(0,1,0)),dm,1,IntVect(3,3,0));
367  }
368 #endif
369 
371  for (int i = 0; i < 3; i++) {
372  mapfac[lev][i]->setVal(myhalf);
373  }
374  for (int i = 3; i < mapfac[lev].size(); i++) {
375  mapfac[lev][i]->setVal(fourth);
376  }
377  } else {
378  for (int i = 0; i < mapfac[lev].size(); i++) {
379  mapfac[lev][i]->setVal(one);
380  }
381  }
382 
384  nudge_data[lev] = std::make_unique<MultiFab>(ba, dm, 4, ngrow_state);
385  nudge_data[lev]->setVal(0.0);
386  }
387 
389  lsf_data[lev] = std::make_unique<MultiFab>(ba, dm, 8, ngrow_state);
390  lsf_data[lev]->setVal(0.0);
391  }
392 
393 
394  // ********************************************************************************************
395  // Build WRF data structures
396  // ********************************************************************************************
397  IntVect ng = vars_new[lev][Vars::cons].nGrowVect();
398 
399  if (lev == 0) {
400  wrf_C1H = std::make_unique<MultiFab>(ba1d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
401  wrf_C2H = std::make_unique<MultiFab>(ba1d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
402  wrf_RDNW = std::make_unique<MultiFab>(ba1d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
403  wrf_MUB = std::make_unique<MultiFab>(ba2d[lev],dm,1,IntVect(ng[0],ng[1],ng[2]));
404  wrf_PHB = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)),dm,1,IntVect(ngrow+1,ngrow+1,0));
405  }
406 
407  mf_PSFC[lev] = std::make_unique<MultiFab>(ba2d[lev],dm,1,ng);
408 
409  //*********************************************************
410  // Variables for Fitch model for windfarm parametrization
411  //*********************************************************
412 #if defined(ERF_USE_WINDFARM)
413  if (solverChoice.windfarm_type == WindFarmType::Fitch){
414  vars_windfarm[lev].define(ba, dm, 5, ngrow_state); // V, dVabsdt, dudt, dvdt, dTKEdt
415  }
416  if (solverChoice.windfarm_type == WindFarmType::EWP){
417  vars_windfarm[lev].define(ba, dm, 3, ngrow_state); // dudt, dvdt, dTKEdt
418  }
419  if (solverChoice.windfarm_type == WindFarmType::SimpleAD) {
420  vars_windfarm[lev].define(ba, dm, 2, ngrow_state);// dudt, dvdt
421  }
422  if (solverChoice.windfarm_type == WindFarmType::GeneralAD) {
423  vars_windfarm[lev].define(ba, dm, 3, ngrow_state);// dudt, dvdt, dwdt
424  }
425  Nturb[lev].define(ba, dm, 1, ngrow_state); // Number of turbines in a cell
426  SMark[lev].define(ba, dm, 2, 1); // Free stream velocity/source term
427  // sampling marker in a cell - 2 components
428 #endif
429 
430  if(solverChoice.init_type == InitType::HindCast and
432 
433  int ncomp_extra = 2;
434  int nvars = vars_new[lev].size();
435 
436  // Resize all containers
437  forecast_state_1[lev].resize(nvars + 1);
438  forecast_state_2[lev].resize(nvars + 1);
439  forecast_state_interp[lev].resize(nvars + 1);
440 
441  // Define the "normal" components
442  for (int comp = 0; comp < nvars; ++comp) {
443  const MultiFab& src = vars_new[lev][comp];
444  ncomp = src.nComp();
445  ngrow = src.nGrow();
446 
447  forecast_state_1[lev][comp].define(ba, dm, ncomp, ng);
448  forecast_state_2[lev][comp].define(ba, dm, ncomp, ng);
449  forecast_state_interp[lev][comp].define(ba, dm, ncomp, ng);
450  }
451 
452  // Define the "extra" component (last slot)
453  {
454  const MultiFab& src0 = vars_new[lev][0];
455  ngrow = src0.nGrow();
456  int idx = nvars;
457 
458  forecast_state_1[lev][idx].define(ba, dm, ncomp_extra, ngrow);
459  forecast_state_2[lev][idx].define(ba, dm, ncomp_extra, ngrow);
460  forecast_state_interp[lev][idx].define(ba, dm, ncomp_extra, ngrow);
461  }
462  bool regrid_forces_file_read = true;
463  WeatherDataInterpolation(lev, t_new[0],z_phys_nd, regrid_forces_file_read);
464  }
465 
466 
467  if(solverChoice.init_type == InitType::HindCast and
469 
470  {
471  const MultiFab& src = vars_new[lev][0];
472  const amrex::DistributionMapping& dm_hc = src.DistributionMap();
473 
474  surface_state_1[lev].define(ba2d[lev], dm_hc, 2, src.nGrow());
475  surface_state_2[lev].define(ba2d[lev], dm_hc, 2, src.nGrow());
476  surface_state_interp[lev].define(ba2d[lev], dm_hc, 2, src.nGrow());
477 
478  bool regrid_forces_file_read = true;
479  SurfaceDataInterpolation(lev, t_new[0], z_phys_nd, regrid_forces_file_read);
480  }
481 
482 #ifdef ERF_USE_WW3_COUPLING
483  // create a new BoxArray and DistributionMapping for a MultiFab with 1 box
484  BoxArray ba_onegrid(geom[lev].Domain());
485  BoxList bl2d_onegrid = ba_onegrid.boxList();
486  for (auto& b : bl2d_onegrid) { b.setRange(2,b.smallEnd(2)); }
487  BoxArray ba2d_onegrid(std::move(bl2d_onegrid));
488  Vector<int> pmap;
489  pmap.resize(1);
490  pmap[0]=0;
491  DistributionMapping dm_onegrid(ba2d_onegrid);
492  dm_onegrid.define(pmap);
493 
494  Hwave_onegrid[lev] = std::make_unique<MultiFab>(ba2d_onegrid,dm_onegrid,1,IntVect(1,1,0));
495  Lwave_onegrid[lev] = std::make_unique<MultiFab>(ba2d_onegrid,dm_onegrid,1,IntVect(1,1,0));
496 
497  BoxList bl2d_wave = ba.boxList();
498  for (auto& b : bl2d_wave) { b.setRange(2,b.smallEnd(2)); }
499  BoxArray ba2d_wave(std::move(bl2d_wave));
500 
501  Hwave[lev] = std::make_unique<MultiFab>(ba2d_wave,dm,1,IntVect(3,3,0));
502  Lwave[lev] = std::make_unique<MultiFab>(ba2d_wave,dm,1,IntVect(3,3,0));
503 
504  std::cout<<ba_onegrid<<std::endl;
505  std::cout<<ba2d_onegrid<<std::endl;
506  std::cout<<dm_onegrid<<std::endl;
507 #endif
508 
509 
510  //*********************************************************
511  // Radiation heating source terms
512  //*********************************************************
513  // Every radiation model (RRTMGP, Simple, TwoStream) writes the same
514  // 2-component (SW, LW) heating rates, so the arrays are shaped the same
515  // way whichever one erf.radiation_model selects.
516  if (solverChoice.rad_type != RadiationType::None)
517  {
518  // Allocate with 1 ghost cell for interpolation stencil (cell_cons_interp)
519  // and FillBoundary operations (needed for nested patches)
520  qheating_rates[lev] = std::make_unique<MultiFab>(ba, dm, 2, 1);
521  // Level layout (RRTMGP's): index k holds the fluxes at the lower
522  // interface of layer k, and the top-of-atmosphere interface sits in
523  // the z-ghost cell above the top layer (k = khi + 1), which is why
524  // the array carries one ghost cell in z. See ERF.H.
525  //
526  // The ghost cells in x and y are not part of that layout. They are here so that
527  // this array can be the coarse source of an InterpFromCoarseLevel, exactly as
528  // qheating_rates is: that overload asserts that the coarse source itself carries
529  // the ghost cells the interpolation stencil reads, and its ParallelCopy then
530  // reads them. See the note in ERF_AdvanceRadiation.cpp.
531  rad_fluxes[lev] = std::make_unique<MultiFab>(ba, dm, 4, IntVect(1,1,1));
532  qheating_rates[lev]->setVal(zero);
533  // Zeroing the ghost cells too is load-bearing, not tidiness: the ghost cells that
534  // lie outside the physical domain are never written by anything else, and they are
535  // read as interpolation-stencil neighbors when this level is a parent.
536  rad_fluxes[lev]->setVal(zero);
537  }
538 
539  // Two-stream radiation: the model owns its 2D surface and SEB fields.
540  if (solverChoice.rad_type == RadiationType::TwoStream)
541  {
543  }
544 
545  //*********************************************************
546  // Turbulent perturbation region initialization
547  //*********************************************************
549  {
550  turbPert.init_tpi_type(lev, solverChoice.pert_type[lev], max_level);
551  turbPert.init_tpi(lev, subdomains[lev], geom[lev].CellSizeArray(),
552  ba, dm, ngrow_state, pp_prefix, refRatio(), max_level);
553  }
554 
555  //
556  // Define the land mask here and set it to all land by default
557  // NOTE: the logic below will BREAK if we have any grids not touching the bottom boundary
558  //
559  {
560  lmask_lev[lev].resize(1);
561  auto ngv = lev_new[Vars::cons].nGrowVect(); ngv[2] = 0;
562  lmask_lev[lev][0] = std::make_unique<iMultiFab>(ba2d[lev],dm,1,ngv);
563  lmask_lev[lev][0]->setVal(solverChoice.is_land[lev]);
564  lmask_lev[lev][0]->FillBoundary(geom[lev].periodicity());
565 
566  land_type_lev[lev].resize(1);
567  land_type_lev[lev][0] = std::make_unique<iMultiFab>(ba2d[lev],dm,1,ngv);
568  land_type_lev[lev][0]->setVal(0);
569  land_type_lev[lev][0]->FillBoundary(geom[lev].periodicity());
570 
571  soil_type_lev[lev].resize(1);
572  soil_type_lev[lev][0] = std::make_unique<iMultiFab>(ba2d[lev],dm,1,ngv);
573  soil_type_lev[lev][0]->setVal(0);
574  soil_type_lev[lev][0]->FillBoundary(geom[lev].periodicity());
575 
576  urb_frac_lev[lev].resize(1);
577  urb_frac_lev[lev][0] = std::make_unique<MultiFab>(ba2d[lev],dm,1,ngv);
578  urb_frac_lev[lev][0]->setVal(one);
579  urb_frac_lev[lev][0]->FillBoundary(geom[lev].periodicity());
580  }
581 
582  // Read in tables needed for windfarm simulations
583  // fill in Nturb multifab - number of turbines in each mesh cell
584  // write out the vtk files for wind turbine location and/or
585  // actuator disks
586  #ifdef ERF_USE_WINDFARM
587  //init_windfarm(lev);
588  #endif
589 
590  if (lev > 0) {
591  fine_mask[lev] = std::make_unique<MultiFab>(grids[lev-1], dmap[lev-1], 1, 0);
592  build_fine_mask(lev, *fine_mask[lev].get());
593  }
594 
595 #ifdef ERF_USE_FFT
596  if ( ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) &&
597  ( (solverChoice.mesh_type == MeshType::ConstantDz) || (solverChoice.mesh_type == MeshType::StretchedDz) ) ) {
598  build_fft_solvers(lev);
599  }
600 #endif
601 }
@ num
Definition: ERF_DataStruct.H:29
#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:21
std::unique_ptr< amrex::MultiFab > wrf_MUB
Definition: ERF.H:1766
std::unique_ptr< amrex::MultiFab > wrf_C1H
Definition: ERF.H:1764
std::unique_ptr< amrex::MultiFab > wrf_PHB
Definition: ERF.H:1768
void build_fine_mask(int lev, amrex::MultiFab &fine_mask)
Definition: ERF_VolWgtSum.cpp:144
void define_column_kextent(int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
Definition: ERF_MakeNewArrays.cpp:637
std::unique_ptr< amrex::MultiFab > wrf_RDNW
Definition: ERF.H:1767
std::unique_ptr< amrex::MultiFab > wrf_C2H
Definition: ERF.H:1765
void define_level(int lev, const RadChoice &rad_choice, amrex::Real rdOcp, const amrex::BoxArray &ba2d, const amrex::DistributionMapping &dm)
Allocate the 2D surface fields of one level on the given horizontal BoxArray and fill them with the s...
Definition: ERF_TwoStreamRadiation.cpp:222
@ num_comps
Definition: ERF_IndexDefines.H:81
amrex::Vector< PerturbationType > pert_type
Turbulent perturbation type for each AMR level.
Definition: ERF_DataStruct.H:2230
RadChoice radChoice
Definition: ERF_DataStruct.H:1976
bool test_mapfactor
Whether to use test map scale factors.
Definition: ERF_DataStruct.H:2018
amrex::Vector< int > is_land
Per-level land/water selector.
Definition: ERF_DataStruct.H:1986
void init_tpi_type(const int lev, const PerturbationType &pert_type, const int max_level)
Store the integer perturbation-type selector for one AMR level.
Definition: ERF_TurbPertStruct.H:41
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)
Initialize turbulent perturbation regions and per-box storage.
Definition: ERF_TurbPertStruct.H:74
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 
)
1336 {
1337  //********************************************************************************************
1338  // Thin immersed body
1339  // *******************************************************************************************
1340 #if 0
1341  if ((solverChoice.advChoice.zero_xflux.size() > 0) ||
1342  (solverChoice.advChoice.zero_yflux.size() > 0) ||
1343  (solverChoice.advChoice.zero_zflux.size() > 0))
1344  {
1345  overset_imask[lev] = std::make_unique<iMultiFab>(ba,dm,1,0);
1346  overset_imask[lev]->setVal(1); // == value is unknown (to be solved)
1347  }
1348 #endif
1349 
1350  if (solverChoice.advChoice.zero_xflux.size() > 0) {
1351  amrex::Print() << "Setting up thin immersed body for "
1352  << solverChoice.advChoice.zero_xflux.size() << " xfaces" << std::endl;
1353  BoxArray ba_xf(ba);
1354  ba_xf.surroundingNodes(0);
1355  thin_xforce[lev] = std::make_unique<MultiFab>(ba_xf,dm,1,0);
1356  thin_xforce[lev]->setVal(0.0);
1357  xflux_imask[lev] = std::make_unique<iMultiFab>(ba_xf,dm,1,0);
1358  xflux_imask[lev]->setVal(1);
1359  for ( MFIter mfi(*xflux_imask[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
1360  {
1361  Array4<int> const& imask_arr = xflux_imask[lev]->array(mfi);
1362  //Array4<int> const& imask_cell_arr = overset_imask[lev]->array(mfi);
1363  Box xbx = mfi.nodaltilebox(0);
1364  for (int iv=0; iv < solverChoice.advChoice.zero_xflux.size(); ++iv) {
1365  const auto& faceidx = solverChoice.advChoice.zero_xflux[iv];
1366  if ((faceidx[0] >= xbx.smallEnd(0)) && (faceidx[0] <= xbx.bigEnd(0)) &&
1367  (faceidx[1] >= xbx.smallEnd(1)) && (faceidx[1] <= xbx.bigEnd(1)) &&
1368  (faceidx[2] >= xbx.smallEnd(2)) && (faceidx[2] <= xbx.bigEnd(2)))
1369  {
1370  imask_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
1371  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
1372  //imask_cell_arr(faceidx[0]-1,faceidx[1],faceidx[2]) = 0;
1373  amrex::AllPrint() << " mask xface at " << faceidx << std::endl;
1374  }
1375  }
1376  }
1377  } else {
1378  thin_xforce[lev] = nullptr;
1379  xflux_imask[lev] = nullptr;
1380  }
1381 
1382  if (solverChoice.advChoice.zero_yflux.size() > 0) {
1383  amrex::Print() << "Setting up thin immersed body for "
1384  << solverChoice.advChoice.zero_yflux.size() << " yfaces" << std::endl;
1385  BoxArray ba_yf(ba);
1386  ba_yf.surroundingNodes(1);
1387  thin_yforce[lev] = std::make_unique<MultiFab>(ba_yf,dm,1,0);
1388  thin_yforce[lev]->setVal(0.0);
1389  yflux_imask[lev] = std::make_unique<iMultiFab>(ba_yf,dm,1,0);
1390  yflux_imask[lev]->setVal(1);
1391  for ( MFIter mfi(*yflux_imask[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
1392  {
1393  Array4<int> const& imask_arr = yflux_imask[lev]->array(mfi);
1394  //Array4<int> const& imask_cell_arr = overset_imask[lev]->array(mfi);
1395  Box ybx = mfi.nodaltilebox(1);
1396  for (int iv=0; iv < solverChoice.advChoice.zero_yflux.size(); ++iv) {
1397  const auto& faceidx = solverChoice.advChoice.zero_yflux[iv];
1398  if ((faceidx[0] >= ybx.smallEnd(0)) && (faceidx[0] <= ybx.bigEnd(0)) &&
1399  (faceidx[1] >= ybx.smallEnd(1)) && (faceidx[1] <= ybx.bigEnd(1)) &&
1400  (faceidx[2] >= ybx.smallEnd(2)) && (faceidx[2] <= ybx.bigEnd(2)))
1401  {
1402  imask_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
1403  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
1404  //imask_cell_arr(faceidx[0],faceidx[1]-1,faceidx[2]) = 0;
1405  amrex::AllPrint() << " mask yface at " << faceidx << std::endl;
1406  }
1407  }
1408  }
1409  } else {
1410  thin_yforce[lev] = nullptr;
1411  yflux_imask[lev] = nullptr;
1412  }
1413 
1414  if (solverChoice.advChoice.zero_zflux.size() > 0) {
1415  amrex::Print() << "Setting up thin immersed body for "
1416  << solverChoice.advChoice.zero_zflux.size() << " zfaces" << std::endl;
1417  BoxArray ba_zf(ba);
1418  ba_zf.surroundingNodes(2);
1419  thin_zforce[lev] = std::make_unique<MultiFab>(ba_zf,dm,1,0);
1420  thin_zforce[lev]->setVal(0.0);
1421  zflux_imask[lev] = std::make_unique<iMultiFab>(ba_zf,dm,1,0);
1422  zflux_imask[lev]->setVal(1);
1423  for ( MFIter mfi(*zflux_imask[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
1424  {
1425  Array4<int> const& imask_arr = zflux_imask[lev]->array(mfi);
1426  //Array4<int> const& imask_cell_arr = overset_imask[lev]->array(mfi);
1427  Box zbx = mfi.nodaltilebox(2);
1428  for (int iv=0; iv < solverChoice.advChoice.zero_zflux.size(); ++iv) {
1429  const auto& faceidx = solverChoice.advChoice.zero_zflux[iv];
1430  if ((faceidx[0] >= zbx.smallEnd(0)) && (faceidx[0] <= zbx.bigEnd(0)) &&
1431  (faceidx[1] >= zbx.smallEnd(1)) && (faceidx[1] <= zbx.bigEnd(1)) &&
1432  (faceidx[2] >= zbx.smallEnd(2)) && (faceidx[2] <= zbx.bigEnd(2)))
1433  {
1434  imask_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
1435  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]) = 0;
1436  //imask_cell_arr(faceidx[0],faceidx[1],faceidx[2]-1) = 0;
1437  amrex::AllPrint() << " mask zface at " << faceidx << std::endl;
1438  }
1439  }
1440  }
1441  } else {
1442  thin_zforce[lev] = nullptr;
1443  zflux_imask[lev] = nullptr;
1444  }
1445 }
amrex::Vector< amrex::IntVect > zero_yflux
Thin immersed-body y-face indices where fluxes are forced to zero.
Definition: ERF_AdvStruct.H:460
amrex::Vector< amrex::IntVect > zero_zflux
Thin immersed-body z-face indices where fluxes are forced to zero.
Definition: ERF_AdvStruct.H:461
amrex::Vector< amrex::IntVect > zero_xflux
Thin immersed-body x-face indices where fluxes are forced to zero.
Definition: ERF_AdvStruct.H:459

◆ init_zphys()

void ERF::init_zphys ( int  lev,
double  elapsed_time 
)
885 {
886  // For EB, z_phys_nd was already initialized with the correct z_offset by init_default_zphys.
887  // The terrain-fitting (BTF) done below is irrelevant for a flat EB mesh and would clobber
888  // the offset, so return early here.
889  if (solverChoice.terrain_type == TerrainType::EB) {
890  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
891  micro->Set_dzmin(lev, dzmin);
892  return;
893  }
894 
895  if (solverChoice.init_type != InitType::WRFInput && solverChoice.init_type != InitType::Metgrid)
896  {
897  if (lev > 0) {
898  //
899  // First interpolate from coarser level if there is one
900  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
901  // have been pre-filled - this includes ghost cells both inside and outside
902  // the domain
903  //
904  InterpFromCoarseLevel(*z_phys_nd[lev], z_phys_nd[lev]->nGrowVect(),
905  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
906  *z_phys_nd[lev-1], 0, 0, 1,
907  geom[lev-1], geom[lev],
908  refRatio(lev-1), &node_bilinear_interp,
910  }
911 
912  //
913  // The STF and Sullivan TF transformations are only defined on a level that
914  // spans the full column, so on a fine level we build the mesh from the one
915  // just interpolated from the coarse level instead. "interpolate" uses that
916  // mesh as-is; "transform" additionally reads the terrain at the fine
917  // resolution and blends it in with a correction that decays with height.
918  //
919  ParmParse pp("erf");
920  int terrain_smoothing = 0;
921  pp.query("terrain_smoothing", terrain_smoothing);
922 
923  FineTerrain fine_terrain = (lev > 0 && terrain_smoothing != 0) ? which_fine_terrain()
925 
926  //
927  // Save the interpolated mesh before the fine terrain overwrites the k=0 slab,
928  // since the transform is expressed relative to it
929  //
930  MultiFab z_phys_interp;
931  if (fine_terrain == FineTerrain::Transform) {
932  z_phys_interp.define(z_phys_nd[lev]->boxArray(), z_phys_nd[lev]->DistributionMap(),
933  1, z_phys_nd[lev]->nGrowVect());
934  MultiFab::Copy(z_phys_interp, *z_phys_nd[lev], 0, 0, 1, 0); // valid region only
935  }
936 
937  if (fine_terrain != FineTerrain::Interpolate) {
938  fill_terrain_surface(lev, *z_phys_nd[lev], elapsed_time);
939  }
940 
942  fine_terrain,
943  (fine_terrain == FineTerrain::Transform) ? &z_phys_interp : nullptr);
944 
945  z_phys_nd[lev]->FillBoundary(geom[lev].periodicity());
946 
947  if (lev == 0) {
948  Real zmax = z_phys_nd[0]->max(0,0,false);
949  Real rel_diff = (zmax - zlevels_stag[0][zlevels_stag[0].size()-1]) / zmax;
950  if (rel_diff > Real(1.e-8)) {
951  amrex::Print() << "max of zphys_nd " << zmax << std::endl;
952  amrex::Print() << "max of zlevels " << zlevels_stag[0][zlevels_stag[0].size()-1] << std::endl;
953  amrex::Warning("Terrain is taller than domain top!");
954  }
955 #if 0
956  // This remains commented out until we verify that the stretched and variable dz pathways
957  // in fact give the same answer when appropriate
958  if (SolverChoice::mesh_type == MeshType::VariableDz)
959  {
960  check_mesh_type(lev);
961  }
962 #endif
963  } // lev == 0
964 
965  } else {
966  // If a WRFInput / met_em file is NOT provided for a finer level, we simply
967  // interpolate the terrain from the coarse level. This is necessary since we
968  // average_down the terrain (see ERF_MakeNewLevel.cpp).
969  //
970  // If a file IS present at this level, the terrain has already been built from
971  // that file's PH + PHB by init_terrain_from_wrfinput (or its metgrid analogue),
972  // which runs BEFORE this routine on both the from-scratch and the from-coarse
973  // paths (init_only precedes init_zphys there because level 0 has to read its
974  // terrain and its data in the same pass). Interpolating from the coarse level
975  // here would therefore throw that away and leave the fine level running on
976  // coarse-derived heights -- so we must not do it.
977  //
978  // NOTE: this is the behavior the comment here has always described; the guard
979  // below is what actually makes it true.
980  if ( (lev > 0) && nc_init_file[lev].empty() ) {
981  //
982  // First interpolate from coarser level if there is one
983  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
984  // have been pre-filled - this includes ghost cells both inside and outside
985  // the domain
986  //
987  InterpFromCoarseLevel(*z_phys_nd[lev], z_phys_nd[lev]->nGrowVect(),
988  z_phys_nd[lev]->nGrowVect(), // DO fill ghost cells outside the domain
989  *z_phys_nd[lev-1], 0, 0, 1,
990  geom[lev-1], geom[lev],
991  refRatio(lev-1), &node_bilinear_interp,
993  }
994  } // init_type
995 
996  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
997  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
998  // Read the small_volfrac threshold from eb2 namespace
999  Real small_volfrac = 0.005;
1000  ParmParse pp_eb2("eb2");
1001  pp_eb2.queryAdd("small_volfrac", small_volfrac);
1002 
1003  // Cell-centered terrain blanking
1004  terrain_blanking[lev]->setVal(one);
1005  const int ng_sub = std::min(ComputeGhostCells(solverChoice) + 2, EBFactory(lev).getVolFrac().nGrow());
1006  MultiFab::Subtract(*terrain_blanking[lev], EBFactory(lev).getVolFrac(), 0, 0, 1, ng_sub);
1007 
1008  // Clip small terrain_blanking values (almost fluid cells) using same threshold as eb2.small_volfrac
1009  if (small_volfrac > zero) {
1010  for (MFIter mfi(*terrain_blanking[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1011  const Box& bx = mfi.tilebox();
1012  auto const& tblank = terrain_blanking[lev]->array(mfi);
1013  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1014  if (tblank(i,j,k) < small_volfrac) { tblank(i,j,k) = zero; }
1015  });
1016  }
1017  }
1018  terrain_blanking[lev]->FillBoundary(geom[lev].periodicity());
1019 
1020 #if USE_FC_FACTORY
1021  // Face-centered terrain blanking from face-centered EB volume fractions
1022  terrain_blanking_xface[lev]->setVal(one);
1023  terrain_blanking_yface[lev]->setVal(one);
1024  terrain_blanking_zface[lev]->setVal(one);
1025 
1026  // Check if face factories are available before using them
1027  auto const* u_factory = eb[lev]->get_u_const_factory();
1028  auto const* v_factory = eb[lev]->get_v_const_factory();
1029  auto const* w_factory = eb[lev]->get_w_const_factory();
1030 
1031  if (u_factory && v_factory && w_factory) {
1032  MultiFab::Subtract(*terrain_blanking_xface[lev], u_factory->getVolFrac(), 0, 0, 1, ng_sub);
1033  MultiFab::Subtract(*terrain_blanking_yface[lev], v_factory->getVolFrac(), 0, 0, 1, ng_sub);
1034  MultiFab::Subtract(*terrain_blanking_zface[lev], w_factory->getVolFrac(), 0, 0, 1, ng_sub);
1035 
1036  // Clip small terrain_blanking values on faces (almost fluid cells) using same threshold
1037  if (small_volfrac > zero) {
1038  for (MFIter mfi(*terrain_blanking_xface[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1039  const Box& xbx = mfi.tilebox();
1040  auto const& tblank_x = terrain_blanking_xface[lev]->array(mfi);
1041  ParallelFor(xbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1042  if (tblank_x(i,j,k) < small_volfrac) { tblank_x(i,j,k) = zero; }
1043  });
1044  }
1045  for (MFIter mfi(*terrain_blanking_yface[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1046  const Box& ybx = mfi.tilebox();
1047  auto const& tblank_y = terrain_blanking_yface[lev]->array(mfi);
1048  ParallelFor(ybx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1049  if (tblank_y(i,j,k) < small_volfrac) { tblank_y(i,j,k) = zero; }
1050  });
1051  }
1052  for (MFIter mfi(*terrain_blanking_zface[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1053  const Box& zbx = mfi.tilebox();
1054  auto const& tblank_z = terrain_blanking_zface[lev]->array(mfi);
1055  ParallelFor(zbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1056  if (tblank_z(i,j,k) < small_volfrac) { tblank_z(i,j,k) = zero; }
1057  });
1058  }
1059  }
1060  }
1061 
1062  terrain_blanking_xface[lev]->FillBoundary(geom[lev].periodicity());
1063  terrain_blanking_yface[lev]->FillBoundary(geom[lev].periodicity());
1064  terrain_blanking_zface[lev]->FillBoundary(geom[lev].periodicity());
1065 #endif
1066 
1067  init_immersed_forcing(lev); // needed for real cases
1068 
1069  // buildings are landmask = 2
1070  for (MFIter mfi(*lmask_lev[lev][0]); mfi.isValid(); ++mfi) {
1071  const Box& bx2d = mfi.growntilebox();
1072  auto lmask_arr = lmask_lev[lev][0]->array(mfi);
1073  const auto& t_blank_arr = terrain_blanking[lev]->array(mfi);
1074 
1075  amrex::ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
1076  // Use k=0 for the terrain_blanking field
1077  if (t_blank_arr(i, j, 0) > 0.0) {
1078  lmask_arr(i, j, k) = 2;
1079  }
1080  });
1081  }
1082  }
1083 
1084  // Compute the min dz and pass to the micro model
1085  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
1086  micro->Set_dzmin(lev, dzmin);
1087 }
FineTerrain
Definition: ERF_TerrainMetrics.H:67
@ Transform
add the fine terrain as a correction that decays with height
@ None
caller has not prepared a fine mesh from the coarse level
@ Interpolate
use the coordinates interpolated from the coarse level as-is
FineTerrain which_fine_terrain()
Definition: ERF_TerrainMetrics.cpp:412
Real get_dzmin_terrain(MultiFab &z_phys_nd)
Definition: ERF_TerrainMetrics.cpp:1073
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, FineTerrain fine_terrain, MultiFab const *z_phys_interp)
Definition: ERF_TerrainMetrics.cpp:231
void check_mesh_type(int lev)
Definition: ERF.cpp:3787
void fill_terrain_surface(int lev, amrex::MultiFab &z_phys, double elapsed_time)
Definition: ERF_MakeNewArrays.cpp:852
Here is the call graph for this function:

◆ InitData()

void ERF::InitData ( )
617 {
618  BL_PROFILE_VAR("ERF::InitData()", InitData);
619  InitData_pre();
620  InitData_post();
621  BL_PROFILE_VAR_STOP(InitData);
622 }
void InitData_pre()
Definition: ERF.cpp:625
void InitData_post()
Definition: ERF.cpp:657
void InitData()
Definition: ERF.cpp:616

Referenced by main().

Here is the caller graph for this function:

◆ InitData_post()

void ERF::InitData_post ( )
658 {
660  {
661  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(finest_level == 0,
662  "Thin immersed body with refinement not currently supported.");
663  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
664  amrex::Print() << "NOTE: Thin immersed body with non-constant dz has not been tested." << std::endl;
665  }
666  }
667 
668  if (!restart_chkfile.empty()) {
669  restart();
670  }
671 
672  // Faces of the resolved buildings, from the blanking both paths have built.
673  init_ibseb();
674 
675  // Select 2-D variables after the active LSM has initialized its runtime
676  // field inventory, including provider-specific soil layers.
677  setPlotVariables2D("plot2d_vars_1", plot2d_var_names_1);
678  setPlotVariables2D("plot2d_vars_2", plot2d_var_names_2);
679 
680  //
681  // Make sure that detJ is the average of the data on a finer level if there is one and if two way coupling
682  //
683  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
684  if (solverChoice.coupling_type == CouplingType::TwoWay) {
685  // NOTE: z_phys_cc is deliberately NOT averaged down. Every level's base state is
686  // built to be in discrete hydrostatic balance against that level's own
687  // cell-centered heights, so replacing the coarse heights with the average of
688  // the fine ones -- after the base states have already been built -- leaves the
689  // coarse base state out of balance with the heights the dycore then uses for
690  // vertical gradients, Rayleigh damping and the sponge zones. z_phys_cc is also
691  // derived from z_phys_nd, which is not averaged down either, so averaging only
692  // the cell-centered heights made the two disagree inside the refined region.
693  //
694  // detJ IS still averaged down: AverageDownTo weights (rho S) by detJ_cc before
695  // averaging and divides by it afterwards, so the coarse detJ must be the average
696  // of the fine detJ for that average-down to telescope and stay conservative.
697  for (int crse_lev = finest_level-1; crse_lev >= 0; crse_lev--) {
698  average_down( *detJ_cc[crse_lev+1], *detJ_cc[crse_lev], 0, 1, refRatio(crse_lev));
699  }
700  }
701  for (int crse_lev = finest_level-1; crse_lev >= 0; crse_lev--) {
702  detJ_cc[crse_lev]->FillBoundary(geom[crse_lev].periodicity());
703  z_phys_cc[crse_lev]->FillBoundary(geom[crse_lev].periodicity());
704  }
705  }
706 
707 #ifdef ERF_IMPLICIT_W
708  if ( (SolverChoice::mesh_type == MeshType::VariableDz) &&
710  for (int lev = 0; lev <= finest_level; lev++) {
711  if ( (solverChoice.vert_implicit_fac[lev][0] > 0) ||
712  (solverChoice.vert_implicit_fac[lev][1] > 0) ||
713  (solverChoice.vert_implicit_fac[lev][2] > 0) )
714  {
715  Warning("Doing implicit solve for u, v, and w with terrain at level " << lev << " -- this has not been tested");
716  }
717  }
718  }
719 #endif
720 
721  //
722  // Copy vars_new into vars_old, then use vars_old to fill covered cells in vars_new during AverageDown
723  //
724  if (SolverChoice::terrain_type == TerrainType::EB) {
725  for (int lev = 0; lev <= finest_level; lev++) {
726  int ncomp_cons = vars_new[lev][Vars::cons].nComp();
727  MultiFab::Copy(vars_old[lev][Vars::cons],vars_new[lev][Vars::cons],0,0,ncomp_cons,vars_new[lev][Vars::cons].nGrowVect());
728  }
729  }
730 
731  if (restart_chkfile.empty()) {
732  if (solverChoice.coupling_type == CouplingType::TwoWay) {
733  AverageDown();
734  }
735  }
736 
737 #ifdef ERF_USE_PARTICLES
738  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
739  // Promote the Lagrangian PC to the multi-level ParGDB before init so
740  // per-level addParticles() can use ParticleBoxArray(lev)/DistributionMap(lev).
741  auto* pc_ptr = dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer();
742  AMREX_ALWAYS_ASSERT(pc_ptr != nullptr);
743  pc_ptr->Define(static_cast<amrex::ParGDBBase*>(GetParGDB()));
744 
746  !restart_chkfile.empty())) {
748  Warning("Tight coupling has not been tested with Lagrangian microphysics");
749  }
750 
751  for (int lev = 0; lev <= finest_level; lev++) {
752  dynamic_cast<LagrangianMicrophysics&>(*micro).initParticles(lev, z_phys_nd[lev]);
753  }
754  }
755  }
756 #endif
757 
758  if (!restart_chkfile.empty()) { // Restart from a checkpoint
759 
760  // Create the physbc objects for {cons, u, v, w, base state}
761  // We fill the additional base state ghost cells just in case we have read the old format
762  for (int lev(0); lev <= finest_level; ++lev) {
763  make_physbcs(lev);
764  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
765  }
766 
768  for (int lev(0); lev <= finest_level; ++lev) {
769  m_forest_drag[lev]->define_drag_field(grids[lev], dmap[lev], geom[lev],
770  z_phys_cc[lev].get(), z_phys_nd[lev].get(),
773  }
774  }
775 
776 #ifdef ERF_USE_NETCDF
777  //
778  // Create the needed bdy_data_xlo etc ... since we don't read it in from checkpoint any more
779  // This follows init_from_wrfinput()
780  //
781  bool use_moist = (solverChoice.moisture_type != MoistureType::None);
782  if (solverChoice.use_real_bcs && solverChoice.init_type == InitType::WRFInput) {
783 
784  if ( geom[0].isPeriodic(0) || geom[0].isPeriodic(1) ) {
785  amrex::Error("Cannot set periodic lateral boundary conditions when reading in real boundary values");
786  }
787 
788  bdy_time_interval = read_times_from_wrfbdy(nc_bdy_file,
789  bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
790  start_bdy_time, final_bdy_time);
791 
792  double time_since_start_bdy = t_new[0] + start_time - start_bdy_time;
793  int n_time_old = static_cast<int>(time_since_start_bdy / bdy_time_interval);
794  MultiFab r_hse(base_state[0], make_alias, BaseState::r0_comp, 1);
795  Array<MultiFab*, AMREX_SPACEDIM> area_vec = {ax[0].get(), ay[0].get(), az[0].get()};
796  auto repack_runtime_bdy = [&] (const int itime) {
797  const bool separate_hydrometeors = solverChoice.use_wrf_bdy_qc_qi &&
799  repack_wrfbdy_to_realbdy(bdy_data_xlo[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
800  repack_wrfbdy_to_realbdy(bdy_data_xhi[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
801  repack_wrfbdy_to_realbdy(bdy_data_ylo[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
802  repack_wrfbdy_to_realbdy(bdy_data_yhi[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
803  };
804 
805  // Need itime=0 for vertical interpolation
806  if (n_time_old > 0) {
807  int itime = 0;
808  bool is_anelastic = (solverChoice.anelastic[0] == 1);
809  read_and_convert_from_wrfbdy(itime,nc_bdy_file,
810  bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
813  r_hse, area_vec, geom[0], use_moist,
819  real_width, bdy_time_interval, is_anelastic);
820  repack_runtime_bdy(itime);
821  }
822 
823  int ntimes = std::min(n_time_old+3, static_cast<int>(bdy_data_xlo.size()));
824 
825  for (int itime = n_time_old; itime < ntimes; itime++)
826  {
827  bool is_anelastic = (solverChoice.anelastic[0] == 1);
828  read_and_convert_from_wrfbdy(itime,nc_bdy_file,
829  bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
832  r_hse, area_vec, geom[0], use_moist,
838  real_width, bdy_time_interval, is_anelastic);
839  if (itime == static_cast<int>(bdy_data_xlo.size())-1 && itime > 0) {
840  repack_runtime_bdy(itime-1);
841  }
842  repack_runtime_bdy(itime);
843  } // itime
844  } // use_real_bcs
845 
846  if (!nc_low_file.empty())
847  {
848  low_time_interval = read_times_from_wrflow(nc_low_file, low_data_zlo, start_low_time, final_low_time);
849 
850  int lev = 0;
851  sst_lev[lev].resize(low_data_zlo.size());
852  tsk_lev[lev].resize(low_data_zlo.size());
853 
854  double time_since_start_low = t_new[0] + start_time - start_low_time;
855  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
856 
857  int ntimes = std::min(n_time_old+3, static_cast<int>(low_data_zlo.size()));
858 
859  for (int itime = n_time_old; itime < ntimes; itime++)
860  {
861  read_from_wrflow(itime, nc_low_file, geom[lev].Domain(), low_data_zlo);
862 
863  // Need to read PSFC
864  FArrayBox NC_fab_var_file;
865  for (int idx = 0; idx < num_boxes_at_level[lev]; idx++) {
866  int success, use_theta_m;
867  read_from_wrfinput(lev, boxes_at_level[lev][idx], nc_init_file[lev][0],
868  NC_fab_var_file, "PSFC", geom[lev],
869  use_theta_m, success);
870  auto& var_fab = NC_fab_var_file;
871 #ifdef _OPENMP
872 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
873 #endif
874  for ( MFIter mfi(*mf_PSFC[lev], false); mfi.isValid(); ++mfi )
875  {
876  FArrayBox &cur_fab = (*mf_PSFC[lev])[mfi];
877  cur_fab.template copy<RunOn::Device>(var_fab, 0, 0, 1);
878  }
879  var_fab.clear();
880  }
881 
882  update_sst_tsk(itime, geom[lev], ba2d[lev],
883  sst_lev[lev], tsk_lev[lev],
884  m_SurfaceLayer[Orientation(Direction::z, Orientation::low)], low_data_zlo,
885  vars_new[lev][Vars::cons], *mf_PSFC[lev],
886  solverChoice.rdOcp, lmask_lev[lev][0], use_moist);
887  } // itime
888  }
889 #endif
890 #ifdef ERF_USE_FFT
891  for (int lev = 0; lev <= finest_level; lev++) {
892  // rebuild fft solvers here in case mesh type was changed when reading the checkpoint file
893  if ( ( (solverChoice.anelastic[lev] == 1) || (solverChoice.project_initial_velocity[lev] == 1) ) &&
894  ( (solverChoice.mesh_type == MeshType::ConstantDz) || (solverChoice.mesh_type == MeshType::StretchedDz) ) ) {
895  build_fft_solvers(lev);
896  }
897  }
898 #endif
899  } // end restart
900 
901 #ifdef ERF_USE_PARTICLES
902  /* If using a Lagrangian microphysics model, its particle container has now been
903  constructed and initialized (calls to micro->Init). So, add its pointer to
904  ERF::particleData and remove its name from list of unallocated particle containers. */
905  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
906  const auto& pc_name( dynamic_cast<LagrangianMicrophysics&>(*micro).getName() );
907  const auto& pc_ptr( dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer() );
908  AMREX_ALWAYS_ASSERT(pc_ptr != nullptr);
909  particleData.pushBack(pc_name, pc_ptr);
910  particleData.getNamesUnalloc().remove(pc_name);
911  }
912 #endif
913 
914  if (input_bndry_planes) {
915  // Read the "time.dat" file to know what data is available
916  m_r2d->read_time_file();
917 
918  // We haven't populated dt yet, set to 0 to ensure assert doesn't crash
919  double dt_dummy = 0.0;
920  m_r2d->read_input_files(t_new[0]+start_time,dt_dummy,m_bc_extdir_vals);
921  }
922 
924  {
925  rhotheta_src.resize(max_level+1);
926  for (int lev = 0; lev <= finest_level; lev++) {
927  BoxList bl_src = vars_new[lev][Vars::cons].boxArray().boxList();
928  for (auto& b : bl_src) {
930  {
931  // source is only defined in Z
932  b.setRange(0, 0, 1);
933  b.setRange(1, 0, 1);
934  }
935  }
936  BoxArray ba_src(std::move(bl_src));
937  rhotheta_src[lev] = std::make_unique<MultiFab>(ba_src, vars_new[lev][Vars::cons].DistributionMap(), 1, 0);
938  rhotheta_src[lev]->setVal(0.);
939  prob->update_rhotheta_sources(t_new[0],
940  rhotheta_src[lev].get(),
941  geom[lev], z_phys_cc[lev]);
942  }
943  }
944 
946  {
947  h_u_geos.resize(max_level+1, Vector<Real>(0));
948  d_u_geos.resize(max_level+1, Gpu::DeviceVector<Real>(0));
949  h_v_geos.resize(max_level+1, Vector<Real>(0));
950  d_v_geos.resize(max_level+1, Gpu::DeviceVector<Real>(0));
951  for (int lev = 0; lev <= finest_level; lev++) {
952  const int domlen = geom[lev].Domain().length(2);
953  h_u_geos[lev].resize(domlen, 0.0_rt);
954  d_u_geos[lev].resize(domlen, 0.0_rt);
955  h_v_geos[lev].resize(domlen, 0.0_rt);
956  d_v_geos[lev].resize(domlen, 0.0_rt);
958  prob->update_geostrophic_profile(t_new[0],
959  h_u_geos[lev], d_u_geos[lev],
960  h_v_geos[lev], d_v_geos[lev],
961  geom[lev], z_phys_cc[lev]);
962  } else {
963  if (SolverChoice::mesh_type == MeshType::VariableDz) {
964  amrex::Print() << "Note: 1-D geostrophic wind profile input is not defined for real terrain" << std::endl;
965  }
967  h_u_geos[lev], d_u_geos[lev],
968  h_v_geos[lev], d_v_geos[lev],
969  geom[lev],
970  zlevels_stag[0]);
971  }
972  }
973  }
974 
976  {
977  rhoqt_src.resize(max_level+1);
978  for (int lev = 0; lev <= finest_level; lev++) {
979  BoxList bl_src = vars_new[lev][Vars::cons].boxArray().boxList();
980  for (auto& b : bl_src) {
982  {
983  // source is only defined in Z
984  b.setRange(0, 0, 1);
985  b.setRange(1, 0, 1);
986  }
987  }
988  BoxArray ba_src(std::move(bl_src));
989  rhoqt_src[lev] = std::make_unique<MultiFab>(ba_src, vars_new[lev][Vars::cons].DistributionMap(), 1, 0);
990  rhoqt_src[lev]->setVal(0.);
991  prob->update_rhoqt_sources(t_new[0],
992  rhoqt_src[lev].get(),
993  geom[lev], z_phys_cc[lev]);
994  }
995  }
996 
998  {
999  h_w_subsid.resize(max_level+1, Vector<Real>(0));
1000  d_w_subsid.resize(max_level+1, Gpu::DeviceVector<Real>(0));
1001  for (int lev = 0; lev <= finest_level; lev++) {
1002  const int domlen = geom[lev].Domain().length(2) + 1; // lives on z-faces
1003  h_w_subsid[lev].resize(domlen, 0.0_rt);
1004  d_w_subsid[lev].resize(domlen, 0.0_rt);
1005  prob->update_w_subsidence(t_new[0],
1006  h_w_subsid[lev], d_w_subsid[lev], base_state[lev],
1007  geom[lev], z_phys_nd[lev]);
1008  }
1009  }
1010 
1012  {
1014  "erf.forcing_timescale must be positive when nudging with large-scale forcing");
1015  if (verbose) {
1016  lsf.verbose_print = true;
1017  }
1019  lsf.interp_forcing(geom[0].data(), zlevels_stag[0], input_sounding_data);
1020  }
1021 
1024  {
1025  for (int lev = 0; lev <= finest_level; lev++) {
1026  initRayleigh_at_level(lev);
1027  }
1028  if (solverChoice.init_type == InitType::Input_Sounding)
1029  {
1030  // Overwrite ubar, vbar, and thetabar with input profiles;
1031  // wbar is assumed to be zero Note: the tau coefficient set by
1032  // prob->erf_init_rayleigh() is still used
1033  bool restarting = (!restart_chkfile.empty());
1034  setRayleighRefFromSounding(restarting);
1035  }
1036  }
1037 
1038  // Read in sponge data from input file
1039  if(solverChoice.spongeChoice.sponge_type == SpongeType::Input_Sponge)
1040  {
1041  initSponge();
1042  bool restarting = (!restart_chkfile.empty());
1043  setSpongeRefFromSounding(restarting);
1044  }
1045 
1047  if (is_it_time_for_action(istep[0], t_new[0], dt[0], pert_interval, -one)) {
1048  turbPert.debug(t_new[0]);
1049  }
1050  }
1051 
1052  // We only write the file at level 0 for now
1053  if (output_bndry_planes)
1054  {
1055  // Create the WriteBndryPlanes object so we can handle writing of boundary plane data
1056  m_w2d = std::make_unique<WriteBndryPlanes>(grids,geom);
1057 
1058  double tot_time = t_new[0]+start_time;
1059  if (tot_time >= bndry_output_planes_start_time) {
1060  bool is_moist = (micro->Get_Qstate_Moist_Size() > 0);
1061  m_w2d->write_planes(0, tot_time, vars_new, is_moist);
1062  }
1063  }
1064 
1065  // Fill boundary conditions in vars_new
1066  for (int lev = 0; lev <= finest_level; ++lev)
1067  {
1068  auto& lev_new = vars_new[lev];
1069 
1070  // ***************************************************************************
1071  // Physical bc's at domain boundary
1072  // ***************************************************************************
1073  IntVect ngvect_cons = vars_new[lev][Vars::cons].nGrowVect();
1074  IntVect ngvect_vels = vars_new[lev][Vars::xvel].nGrowVect();
1075 
1076  int ncomp_cons = lev_new[Vars::cons].nComp();
1077  bool do_fb = true;
1078 
1079 #ifdef ERF_USE_NETCDF
1080  if (solverChoice.use_real_bcs && (lev==0)) {
1081  int icomp_cons = 0;
1082  bool cons_only = false;
1083  Vector<MultiFab*> mfs_vec = {&lev_new[Vars::cons],&lev_new[Vars::xvel],
1084  &lev_new[Vars::yvel],&lev_new[Vars::zvel]};
1085  fill_from_realbdy(mfs_vec,t_new[lev],cons_only,icomp_cons,
1086  ncomp_cons,ngvect_cons,ngvect_vels);
1087  do_fb = false;
1088  }
1089 #endif
1090 
1091  (*physbcs_cons[lev])(lev_new[Vars::cons],lev_new[Vars::xvel],lev_new[Vars::yvel],0,ncomp_cons,
1092  ngvect_cons,t_new[lev],BCVars::cons_bc,do_fb);
1093  ( *physbcs_u[lev])(lev_new[Vars::xvel],lev_new[Vars::xvel],lev_new[Vars::yvel],
1094  ngvect_vels,t_new[lev],BCVars::xvel_bc,do_fb);
1095  ( *physbcs_v[lev])(lev_new[Vars::yvel],lev_new[Vars::xvel],lev_new[Vars::yvel],
1096  ngvect_vels,t_new[lev],BCVars::yvel_bc,do_fb);
1097  ( *physbcs_w[lev])(lev_new[Vars::zvel],lev_new[Vars::xvel],lev_new[Vars::yvel],
1098  ngvect_vels,t_new[lev],BCVars::zvel_bc,do_fb);
1099  }
1100 
1101  //
1102  // If we are starting from scratch, we have the option to project the initial velocity field
1103  // regardless of how we initialized. Note that project_initial_velocity operates on vars_new.
1104  // pp_inc is used as scratch space here; we zero it out after the projection
1105  //
1106  if (restart_chkfile == "")
1107  {
1108  for (int lev = 0; lev <= finest_level; ++lev)
1109  {
1110  if (solverChoice.project_initial_velocity[lev] == 1) {
1111  double dummy_dt = 1.0;
1112  if (verbose > 0) {
1113  amrex::Print() << "Projecting initial velocity field at level " << lev << std::endl;
1114  }
1115 
1116  project_initial_velocity(lev, t_new[lev], dummy_dt);
1117 
1118  pp_inc[lev].setVal(0.);
1119  gradp[lev][GpVars::gpx].setVal(0.);
1120  gradp[lev][GpVars::gpy].setVal(0.);
1121  gradp[lev][GpVars::gpz].setVal(0.);
1122  } // project
1123  } // lev
1124  }
1125 
1126  // Copy from new into old just in case (after filling boundary conditions and possibly projecting)
1127  for (int lev = 0; lev <= finest_level; ++lev)
1128  {
1129  int nc = vars_new[lev][Vars::cons].nComp();
1130 
1131  MultiFab::Copy(vars_old[lev][Vars::cons],vars_new[lev][Vars::cons],0,0,nc,vars_new[lev][Vars::cons].nGrowVect());
1132  MultiFab::Copy(vars_old[lev][Vars::xvel],vars_new[lev][Vars::xvel],0,0, 1,vars_new[lev][Vars::xvel].nGrowVect());
1133  MultiFab::Copy(vars_old[lev][Vars::yvel],vars_new[lev][Vars::yvel],0,0, 1,vars_new[lev][Vars::yvel].nGrowVect());
1134  MultiFab::Copy(vars_old[lev][Vars::zvel],vars_new[lev][Vars::zvel],0,0, 1,vars_new[lev][Vars::zvel].nGrowVect());
1135  }
1136 
1137  // Compute the minimum dz in the domain at each level (to be used for setting the timestep)
1138  dz_min.resize(max_level+1);
1139  for (int lev = 0; lev <= finest_level; ++lev)
1140  {
1141  dz_min[lev] = geom[lev].CellSize(2);
1142  if ( SolverChoice::mesh_type != MeshType::ConstantDz ) {
1143  dz_min[lev] *= (*detJ_cc[lev]).min(0);
1144  }
1145  }
1146 
1147 
1148  // We don't need to recompute dt[lev] on restart because we read it in from the checkpoint file.
1149  if (restart_chkfile.empty()) {
1150  ComputeDt();
1151  }
1152 
1153  // Check the viscous limit
1157  Real delta = std::min({geom[finest_level].CellSize(0),
1158  geom[finest_level].CellSize(1),
1159  dz_min[finest_level]});
1160  if (dc.dynamic_viscosity == 0) {
1161  Print() << "Note: Molecular diffusion specified but dynamic_viscosity has not been specified" << std::endl;
1162  } else {
1163  Real nu = dc.dynamic_viscosity / dc.rho0_trans;
1164  Real viscous_limit = two * delta*delta / nu;
1165  Print() << "smallest grid spacing at level " << finest_level << " = " << delta << std::endl;
1166  Print() << "dt at level " << finest_level << " = " << dt[finest_level] << std::endl;
1167  Print() << "Viscous CFL is " << dt[finest_level] / viscous_limit << std::endl;
1168  if (fixed_dt[finest_level] >= viscous_limit) {
1169  Warning("Specified fixed_dt is above the viscous limit");
1170  } else if (dt[finest_level] >= viscous_limit) {
1171  Warning("Adaptive dt based on convective CFL only is above the viscous limit");
1172  }
1173  }
1174  }
1175 
1176  // Fill ghost cells/faces
1177  for (int lev = 0; lev <= finest_level; ++lev)
1178  {
1179  if (lev > 0 && cf_width >= 0) {
1181  }
1182 
1183  auto& lev_new = vars_new[lev];
1184 
1185  //
1186  // Fill boundary conditions -- not sure why we need this here
1187  //
1188  bool fillset = false;
1189  if (lev == 0) {
1190  FillPatchCrseLevel(lev, t_new[lev],
1191  {&lev_new[Vars::cons],&lev_new[Vars::xvel],&lev_new[Vars::yvel],&lev_new[Vars::zvel]});
1192  } else {
1193  FillPatchFineLevel(lev, t_new[lev],
1194  {&lev_new[Vars::cons],&lev_new[Vars::xvel],&lev_new[Vars::yvel],&lev_new[Vars::zvel]},
1195  {&lev_new[Vars::cons],&rU_new[lev],&rV_new[lev],&rW_new[lev]},
1196  base_state[lev], base_state[lev],
1197  fillset);
1198  }
1199 
1200  //
1201  // We do this here to make sure level (lev-1) boundary conditions are filled
1202  // before we interpolate to level (lev) ghost cells
1203  //
1204  if (lev < finest_level) {
1205  auto& lev_old = vars_old[lev];
1206  MultiFab::Copy(lev_old[Vars::cons],lev_new[Vars::cons],0,0,lev_old[Vars::cons].nComp(),lev_old[Vars::cons].nGrowVect());
1207  MultiFab::Copy(lev_old[Vars::xvel],lev_new[Vars::xvel],0,0,lev_old[Vars::xvel].nComp(),lev_old[Vars::xvel].nGrowVect());
1208  MultiFab::Copy(lev_old[Vars::yvel],lev_new[Vars::yvel],0,0,lev_old[Vars::yvel].nComp(),lev_old[Vars::yvel].nGrowVect());
1209  MultiFab::Copy(lev_old[Vars::zvel],lev_new[Vars::zvel],0,0,lev_old[Vars::zvel].nComp(),lev_old[Vars::zvel].nGrowVect());
1210  }
1211 
1212  //
1213  // We fill the ghost cell values of the base state in case it wasn't done in the initialization
1214  //
1215  base_state[lev].FillBoundary(geom[lev].periodicity());
1216 
1217  // For moving terrain only
1218  if (solverChoice.terrain_type == TerrainType::MovingFittedMesh) {
1219  MultiFab::Copy(base_state_new[lev],base_state[lev],0,0,BaseState::num_comps,base_state[lev].nGrowVect());
1220  base_state_new[lev].FillBoundary(geom[lev].periodicity());
1221  }
1222 
1223  }
1224 
1225  // If lev > 0, we need to fill bc's by interpolation from coarser grid
1226  for (int lev = 1; lev <= finest_level; ++lev)
1227  {
1228  Interp2DArrays(lev,ba2d[lev],dmap[lev]);
1229  } // lev
1230 
1231 #ifdef ERF_USE_WW3_COUPLING
1232  int my_lev = 0;
1233  amrex::Print() << " About to call send_to_ww3 from ERF.cpp" << std::endl;
1234  send_to_ww3(my_lev);
1235  amrex::Print() << " About to call read_waves from ERF.cpp" << std::endl;
1236  read_waves(my_lev);
1237  // send_to_ww3(my_lev);
1238 #endif
1239 
1240  // Create wall distance field for RANS model
1241  for (int lev = 0; lev <= finest_level; lev++) {
1242  if (solverChoice.turbChoice[lev].rans_type != RANSType::None) {
1243  // Handle bottom boundary
1244  poisson_wall_dist(lev);
1245 
1246  // Correct the wall distance for immersed bodies
1252  geom[lev],
1253  z_phys_cc[lev]);
1254 
1255  // The correction is only applied on the valid region
1256  fill_wall_dist_ghost_cells(*walldist[lev], geom[lev]);
1257  }
1258  }
1259  }
1260 
1261  // Configure SurfaceLayer params if used
1262  // NOTE: we must set up the MOST routine after calling FillPatch
1263  // in order to have lateral ghost cells filled (MOST + terrain interp).
1264  bool updated_prim = false;
1265  // Count number of surface layer boundaries to determine correct parser prefix
1266  int n_faces = 0;
1267  amrex::GpuArray<int, AMREX_SPACEDIM*2> surface_layer_faces{};
1268  for (OrientationIter oit; oit; ++oit) {
1269  Orientation ori = oit();
1270  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
1271  n_faces += 1;
1272  surface_layer_faces[static_cast<int>(ori)] = 1;
1273  }
1274  }
1275 
1276  // With multiple surface-layer faces, face-qualified prefixes are normally
1277  // required. Preserve the historical unqualified zlo inputs when a user adds
1278  // another surface-layer face. What the user actually wrote is read from the
1279  // snapshot taken in ReadParameters; see has_surface_layer_inputs for why the
1280  // live ParmParse table cannot be asked this question here.
1281  bool use_legacy_zlo_prefix = false;
1282  if (n_faces > 1 &&
1283  phys_bc_type[Orientation::zlo()] == ERF_BC::surface_layer) {
1284  const bool has_legacy_inputs = has_surface_layer_inputs(pp_prefix);
1285  const bool has_zlo_inputs = has_surface_layer_inputs(
1286  pp_prefix + "." + BoundaryFaceName[Orientation::zlo()]);
1287 
1288  if (has_legacy_inputs && has_zlo_inputs) {
1289  Abort("Both legacy unqualified and zlo-qualified surface-layer inputs "
1290  "are present. Use only erf.zlo.most.* and/or "
1291  "erf.zlo.surface_layer.* when multiple surface-layer faces "
1292  "are enabled.");
1293  }
1294 
1295  if (has_legacy_inputs) {
1296  use_legacy_zlo_prefix = true;
1297  Warning("Multiple surface-layer faces are enabled while using legacy "
1298  "unqualified surface-layer inputs. Applying erf.most.* and "
1299  "erf.surface_layer.* to zlo; migrate them to erf.zlo.most.* "
1300  "and erf.zlo.surface_layer.*.");
1301  }
1302  }
1303 
1304  for (OrientationIter oit; oit; ++oit) {
1305  Orientation ori = oit();
1306  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
1308  (solverChoice.turbChoice[0].les_type != LESType::None) ||
1309  (solverChoice.turbChoice[0].rans_type != RANSType::None) ||
1310  (solverChoice.turbChoice[0].pbl_type != PBLType::None) );
1311  AMREX_ALWAYS_ASSERT(has_diff);
1312 
1313  bool rotate = solverChoice.use_rotate_surface_flux;
1314  if (rotate) {
1315  Print() << "Using surface layer model with stress rotations" << std::endl;
1316  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ori.coordDir() == 2 && ori.faceDir() == Orientation::Side::low,
1317  "Surface layer with stress rotations can only be enabled for the bottom z face");
1318  }
1319 
1320  //
1321  // This constructor will make the SurfaceLayer object but not allocate the arrays at each level.
1322  //
1323  // Build vector of eb pointers for all levels
1324  amrex::Vector<const eb_*> eb_ptrs;
1325  eb_ptrs.resize(finest_level + 1, nullptr);
1326  if (solverChoice.terrain_type == TerrainType::EB) {
1327  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ori.coordDir() == 2 && ori.faceDir() == Orientation::Side::low,
1328  "Surface layer with EB can only be enabled for the bottom z face");
1329  for (int lev = 0; lev <= finest_level; lev++) {
1330  eb_ptrs[lev] = eb[lev] ? eb[lev].get() : nullptr;
1331  }
1332  }
1333 
1334  // Keep the historical unqualified prefix for a single zlo face,
1335  // and for multiple faces when legacy zlo inputs were detected.
1336  std::string face_pp_prefix(pp_prefix + "." + BoundaryFaceName[ori]);
1337  if ((n_faces == 1 || use_legacy_zlo_prefix) &&
1338  static_cast<int>(ori) == Orientation::zlo()) {
1339  face_pp_prefix = pp_prefix;
1340  }
1341  m_SurfaceLayer[ori] = std::make_unique<SurfaceLayer>(ori, geom, rotate, face_pp_prefix, Qv_prim,
1342  z_phys_nd,
1343  zlevels_stag,
1346  solverChoice.turbChoice[finest_level],
1348 #ifdef ERF_USE_NETCDF
1349  start_low_time, final_low_time, low_time_interval,
1350 #else
1351  zero, zero, zero,
1352 #endif
1353  eb_ptrs);
1354  m_SurfaceLayer[ori]->set_surface_layer_faces(surface_layer_faces);
1355  m_SurfaceLayer[ori]->set_coupled_sst_active(solverChoice.use_coupled_sst &&
1356  static_cast<int>(ori) == Orientation::zlo());
1357  // This call will allocate the arrays at each level. If we regrid later, either changing
1358  // the number of levels or just the grids at each existing level, we will call an update routine
1359  // to redefine the internal arrays in m_SurfaceLayer.
1360  for (int lev = 0; lev <= finest_level; lev++)
1361  {
1362  Vector<MultiFab*> mfv_old = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::xvel],
1363  &vars_old[lev][Vars::yvel], &vars_old[lev][Vars::zvel]};
1364  m_SurfaceLayer[ori]->make_SurfaceLayer_at_level(lev,finest_level+1,
1365  mfv_old, Theta_prim[lev], Qv_prim[lev],
1366  Qr_prim[lev], z_phys_nd[lev],
1367  Hwave[lev].get(),Lwave[lev].get(),eddyDiffs_lev[lev].get(),
1369  sst_lev[lev], tsk_lev[lev], lmask_lev[lev]);
1370  }
1371 
1372  // If initializing from an input_sounding, make sure the surface layer
1373  // is using the same surface conditions
1374  // Note: do this only if using a single face on zlo, otherwise this will overwrite user specified wall values
1375  if (n_faces == 1 && static_cast<int>(ori) == Orientation::zlo()) {
1376  if (solverChoice.init_type == InitType::Input_Sounding) {
1379  for (int lev = 0; lev <= finest_level; lev++) {
1380  m_SurfaceLayer[ori]->set_t_surf(lev, theta0);
1381  m_SurfaceLayer[ori]->set_q_surf(lev, qv0);
1382  }
1383  }
1384  }
1385 
1386  // We now configure ABLMost params here so that we can print the averages at t=0
1387  // Note we don't fill ghost cells here because this is just for diagnostics
1388  for (int lev = 0; lev <= finest_level; ++lev)
1389  {
1390  IntVect ng = Theta_prim[lev]->nGrowVect();
1391 
1392  if (!updated_prim) {
1393  // This only needs to be done once (Theta,Qv_prim,Qr_prim should only be calculated once and reused for other faces)
1394  MultiFab::Copy( *Theta_prim[lev], vars_new[lev][Vars::cons], RhoTheta_comp, 0, 1, ng);
1395  MultiFab::Divide(*Theta_prim[lev], vars_new[lev][Vars::cons], Rho_comp, 0, 1, ng);
1396 
1397  if (solverChoice.moisture_type != MoistureType::None) {
1398  ng = Qv_prim[lev]->nGrowVect();
1399 
1400  MultiFab::Copy( *Qv_prim[lev], vars_new[lev][Vars::cons], RhoQ1_comp, 0, 1, ng);
1401  MultiFab::Divide(*Qv_prim[lev], vars_new[lev][Vars::cons], Rho_comp, 0, 1, ng);
1402 
1403  int rhoqr_comp = solverChoice.moisture_indices.qr;
1404  if (rhoqr_comp > -1) {
1405  MultiFab::Copy( *Qr_prim[lev], vars_new[lev][Vars::cons], rhoqr_comp, 0, 1, ng);
1406  MultiFab::Divide(*Qr_prim[lev], vars_new[lev][Vars::cons], Rho_comp, 0, 1, ng);
1407  } else {
1408  Qr_prim[lev]->setVal(0.0);
1409  }
1410  }
1411  }
1412  m_SurfaceLayer[ori]->update_mac_ptrs(lev, vars_new, Theta_prim, Qv_prim, Qr_prim);
1413 
1414  if (restart_chkfile == "") {
1415  // Only do this if starting from scratch; if restarting, then
1416  // we don't want to call update_fluxes multiple times because
1417  // it will change u* and theta* from their previous values
1418  m_SurfaceLayer[ori]->update_pblh(lev, vars_new, z_phys_cc[lev].get(),
1420 #ifdef ERF_USE_NETCDF
1421  double elapsed_time_since_start_low = t_new[lev] + (start_time - start_low_time);
1422 #else
1423  double elapsed_time_since_start_low = t_new[lev] + start_time;
1424 #endif
1425  m_SurfaceLayer[ori]->update_fluxes(lev, t_new[lev], elapsed_time_since_start_low,
1426  vars_new[lev][Vars::cons],
1427  z_phys_nd[lev],
1428  walldist[lev]);
1429 
1430  if (ori.coordDir() == 2 && ori.faceDir() == Orientation::Side::low) {
1431  // Initialize tke(x,y,z) as a function of u*(x,y)
1432  if (solverChoice.turbChoice[lev].init_tke_from_ustar) {
1433  Real qkefac = one;
1434  if (solverChoice.turbChoice[lev].pbl_type == PBLType::MYNN25 ||
1435  solverChoice.turbChoice[lev].pbl_type == PBLType::MYNNEDMF)
1436  {
1437  // https://github.com/NCAR/MYNN-EDMF/blob/90f36c25259ec1960b24325f5b29ac7c5adeac73/module_bl_mynnedmf.F90#L1325-L1333
1438  const Real B1 = solverChoice.turbChoice[lev].pbl_mynn.B1;
1439  qkefac = Real(1.5) * std::pow(B1, two/three);
1440  }
1441  m_SurfaceLayer[ori]->init_tke_from_ustar(lev, vars_new[lev][Vars::cons], z_phys_nd[lev], qkefac);
1442  }
1443  }
1444  }
1445  }
1446  updated_prim = true;
1447  } else {
1448  m_SurfaceLayer[ori] = nullptr;
1449  }
1450  } // end if (phys_bc_type[Orientation(Direction::z,Orientation::low)] == ERF_BC::surface_layer)
1451 
1452  if (!restart_chkfile.empty()) {
1453  // All active faces now exist, so restore every surface-layer field once.
1455  }
1456 
1457  // Update micro vars and finish moisture model initializations before first plot file
1458  if (solverChoice.moisture_type != MoistureType::None) {
1459  for (int lev = 0; lev <= finest_level; ++lev) {
1460  const amrex::MultiFab* base = solverChoice.anelastic[lev] ? &base_state[lev] : nullptr;
1461  micro->Update_Micro_Vars_Lev(lev, vars_new[lev][Vars::cons], base);
1462  micro->FinishInit(lev, vars_new[lev][Vars::cons], z_phys_nd);
1463  }
1464  }
1465 
1466  // Fill time averaged velocities before first plot file
1467  //
1468  // NOTE: only when starting from scratch. On restart the running sum and its
1469  // normalizer have just been read back in, and that sum already includes
1470  // the state at the checkpoint time (ERF::Advance accumulates after each
1471  // step). Accumulating it again here would count that state twice, so a
1472  // restarted run would not match the equivalent continuous run, and the
1473  // bias would compound over a chain of restarts.
1474  if (solverChoice.time_avg_vel && restart_chkfile.empty()) {
1475  for (int lev = 0; lev <= finest_level; ++lev) {
1476  Time_Avg_Vel_atCC(dt[lev], t_avg_cnt[lev], vel_t_avg[lev].get(),
1477  vars_new[lev][Vars::xvel],
1478  vars_new[lev][Vars::yvel],
1479  vars_new[lev][Vars::zvel]);
1480  }
1481  }
1482 
1484  if (solverChoice.mean_vars_reset_mode == "time" &&
1485  static_cast<double>(t_new[0]) >=
1486  static_cast<double>(solverChoice.mean_vars_reset_time)) {
1487  if (mean_vars_time_reset_done == 0) {
1490  }
1491  }
1492  // A restart restores the already accumulated window from the
1493  // checkpoint. Sampling the restart state here would count that
1494  // instant twice; a fresh run still needs its initial sample for the
1495  // historical t=0 behavior.
1496  if (restart_chkfile.empty()) {
1497  for (int lev = 0; lev <= finest_level; ++lev) {
1499  vars_new[lev][Vars::xvel],
1500  vars_new[lev][Vars::yvel],
1501  vars_new[lev][Vars::zvel],
1502  vars_new[lev][Vars::cons]);
1503  }
1504  }
1505  }
1506 
1507 #ifdef ERF_USE_PARTICLES
1508  // Redistribute particles so the container has valid data at all AMR levels
1509  // before the initial plotfile write
1510  if (finest_level > 0) {
1511  particleData.Redistribute(z_phys_nd);
1512  }
1513 #endif
1514 
1515  // Print max values of lateral gradients of base state pressure at level 0
1516  if (verbose > 0) {
1517  for (int lev = 0; lev <= finest_level; ++lev) {
1518  if (lev == 0) {
1521  }
1522  }
1523  }
1524 
1525  // check for additional plotting variables that are available after particle containers
1526  // are setup.
1527  const std::string& pv3d_1 = "plot_vars_1" ; appendPlotVariables(pv3d_1,plot3d_var_names_1);
1528  const std::string& pv3d_2 = "plot_vars_2" ; appendPlotVariables(pv3d_2,plot3d_var_names_2);
1529  const std::string& pv2d_1 = "plot2d_vars_1"; appendPlotVariables(pv2d_1,plot2d_var_names_1);
1530  const std::string& pv2d_2 = "plot2d_vars_2"; appendPlotVariables(pv2d_2,plot2d_var_names_2);
1531 
1532  if ( restart_chkfile.empty() && (m_check_int > 0 || m_check_per > zero) )
1533  {
1537  }
1538 
1539  if ( (restart_chkfile.empty()) ||
1540  (!restart_chkfile.empty() && plot_file_on_restart) )
1541  {
1542  int plotfiles_3d_written = 0;
1543  bool interval_diagnostic_consumed = false;
1544  if (m_plot3d_int_1 > 0 || m_plot3d_per_1 > zero)
1545  {
1546  const bool wrote_plotfile = Write3DPlotFile(1,plotfile3d_type_1,plot3d_var_names_1);
1547  if (wrote_plotfile) {
1548  ++plotfiles_3d_written;
1549  interval_diagnostic_consumed = interval_diagnostic_consumed ||
1551  }
1554  }
1555  if (m_plot3d_int_2 > 0 || m_plot3d_per_2 > zero)
1556  {
1557  const bool wrote_plotfile = Write3DPlotFile(2,plotfile3d_type_2,plot3d_var_names_2);
1558  if (wrote_plotfile) {
1559  ++plotfiles_3d_written;
1560  interval_diagnostic_consumed = interval_diagnostic_consumed ||
1562  }
1565  }
1566  // A restart-only plot is observational: it must not consume the
1567  // restored interval window before the first post-restart advance.
1569  plotfiles_3d_written,
1571  !restart_chkfile.empty(), interval_diagnostic_consumed));
1572  if (m_plot2d_int_1 > 0 || m_plot2d_per_1 > zero)
1573  {
1577  }
1578  if (m_plot2d_int_2 > 0 || m_plot2d_per_2 > zero)
1579  {
1583  }
1584  for (int i = 0; i < m_subvol_int.size(); i++) {
1585  if (m_subvol_int[i] > 0 || m_subvol_per[i] > zero) {
1587  last_subvol_step[i] = istep[0];
1588  if (m_subvol_per[i] > zero) {last_subvol_time[i] += m_subvol_per[i];}
1589  }
1590  }
1591  }
1592 
1593  // Set these up here because we need to know which MPI rank "cell" is on...
1594  ParmParse pp("erf");
1595  if (pp.contains("data_log"))
1596  {
1597  int num_datalogs = pp.countval("data_log");
1598  datalog.resize(num_datalogs);
1599  datalogname.resize(num_datalogs);
1600  pp.queryarr("data_log",datalogname,0,num_datalogs);
1601  for (int i = 0; i < num_datalogs; i++) {
1603  }
1604  }
1605 
1606  if (pp.contains("der_data_log"))
1607  {
1608  int num_der_datalogs = pp.countval("der_data_log");
1609  der_datalog.resize(num_der_datalogs);
1610  der_datalogname.resize(num_der_datalogs);
1611  pp.queryarr("der_data_log",der_datalogname,0,num_der_datalogs);
1612  for (int i = 0; i < num_der_datalogs; i++) {
1614  }
1615  }
1616 
1617  if (pp.contains("energy_data_log"))
1618  {
1619  int num_energy_datalogs = pp.countval("energy_data_log");
1620  tot_e_datalog.resize(num_energy_datalogs);
1621  tot_e_datalogname.resize(num_energy_datalogs);
1622  pp.queryarr("energy_data_log",tot_e_datalogname,0,num_energy_datalogs);
1623  for (int i = 0; i < num_energy_datalogs; i++) {
1625  }
1626  }
1627 
1629  {
1630  // Create data log for radiation model if requested
1631  rad[0]->setupDataLog();
1632  }
1633 
1634 
1635  if (restart_chkfile.empty() && profile_int > 0) {
1636  if (destag_profiles) {
1637  // all variables cell-centered
1639  } else {
1640  // some variables staggered
1642  }
1643  }
1644 
1645  if (pp.contains("sample_point_log") && pp.contains("sample_point"))
1646  {
1647  int lev = 0;
1648 
1649  int num_samplepts = pp.countval("sample_point") / AMREX_SPACEDIM;
1650  if (num_samplepts > 0) {
1651  Vector<int> index; index.resize(num_samplepts*AMREX_SPACEDIM);
1652  samplepoint.resize(num_samplepts);
1653 
1654  pp.queryarr("sample_point",index,0,num_samplepts*AMREX_SPACEDIM);
1655  for (int i = 0; i < num_samplepts; i++) {
1656  IntVect iv(index[AMREX_SPACEDIM*i+0],index[AMREX_SPACEDIM*i+1],index[AMREX_SPACEDIM*i+2]);
1657  samplepoint[i] = iv;
1658  }
1659  }
1660 
1661  int num_sampleptlogs = pp.countval("sample_point_log");
1662  AMREX_ALWAYS_ASSERT(num_sampleptlogs == num_samplepts);
1663  if (num_sampleptlogs > 0) {
1664  sampleptlog.resize(num_sampleptlogs);
1665  sampleptlogname.resize(num_sampleptlogs);
1666  pp.queryarr("sample_point_log",sampleptlogname,0,num_sampleptlogs);
1667 
1668  for (int i = 0; i < num_sampleptlogs; i++) {
1670  }
1671  }
1672 
1673  }
1674 
1675  bool has_sample_line = pp.contains("sample_line");
1676  bool has_sample_line_real = pp.contains("sample_line_real");
1677  if (has_sample_line && has_sample_line_real) {
1678  Abort("Specify only one of erf.sample_line or erf.sample_line_real");
1679  }
1680 
1681  if (pp.contains("sample_line_log") && (has_sample_line || has_sample_line_real))
1682  {
1683  int lev = 0;
1684 
1685  int num_samplelines = 0;
1686  if (has_sample_line) {
1687  num_samplelines = pp.countval("sample_line") / AMREX_SPACEDIM;
1688  if (num_samplelines > 0) {
1689  Vector<int> index; index.resize(num_samplelines*AMREX_SPACEDIM);
1690  sampleline.resize(num_samplelines);
1691 
1692  pp.queryarr("sample_line",index,0,num_samplelines*AMREX_SPACEDIM);
1693  for (int i = 0; i < num_samplelines; i++) {
1694  IntVect iv(index[AMREX_SPACEDIM*i+0],index[AMREX_SPACEDIM*i+1],index[AMREX_SPACEDIM*i+2]);
1695  sampleline[i] = iv;
1696  }
1697  }
1698  } else {
1699  int num_real_vals = pp.countval("sample_line_real");
1700  if (num_real_vals % AMREX_SPACEDIM != 0) {
1701  Abort("erf.sample_line_real must be specified as (x,y,z) triples");
1702  }
1703 
1704  num_samplelines = num_real_vals / AMREX_SPACEDIM;
1705  if (num_samplelines > 0) {
1706  Vector<Real> location; location.resize(num_real_vals);
1707  sampleline.resize(num_samplelines);
1708 
1709  pp.queryarr("sample_line_real",location,0,num_real_vals);
1710 
1711  const Box& domain = geom[lev].Domain();
1712  const auto* prob_lo = geom[lev].ProbLo();
1713  const auto* prob_hi = geom[lev].ProbHi();
1714  const auto* dx = geom[lev].CellSize();
1715 
1716  for (int i = 0; i < num_samplelines; i++) {
1717  Real xloc = location[AMREX_SPACEDIM*i+0];
1718  Real yloc = location[AMREX_SPACEDIM*i+1];
1719  Real zloc = location[AMREX_SPACEDIM*i+2];
1720 
1721  if (xloc < prob_lo[0] || xloc > prob_hi[0] ||
1722  yloc < prob_lo[1] || yloc > prob_hi[1] ||
1723  zloc < prob_lo[2] || zloc > prob_hi[2]) {
1724  Abort("erf.sample_line_real must lie within the level-0 domain");
1725  }
1726 
1727  int i_cell = domain.smallEnd(0) + static_cast<int>(std::floor((xloc - prob_lo[0]) / dx[0]));
1728  int j_cell = domain.smallEnd(1) + static_cast<int>(std::floor((yloc - prob_lo[1]) / dx[1]));
1729  int k_cell = domain.smallEnd(2) + static_cast<int>(std::floor((zloc - prob_lo[2]) / dx[2]));
1730 
1731  i_cell = std::min(i_cell, domain.bigEnd(0));
1732  j_cell = std::min(j_cell, domain.bigEnd(1));
1733  k_cell = std::min(k_cell, domain.bigEnd(2));
1734 
1735  sampleline[i] = IntVect(i_cell, j_cell, k_cell);
1736  }
1737  }
1738  }
1739 
1740  int num_samplelinelogs = pp.countval("sample_line_log");
1741  AMREX_ALWAYS_ASSERT(num_samplelinelogs == num_samplelines);
1742  if (num_samplelinelogs > 0) {
1743  samplelinelog.resize(num_samplelinelogs);
1744  samplelinelogname.resize(num_samplelinelogs);
1745  pp.queryarr("sample_line_log",samplelinelogname,0,num_samplelinelogs);
1746 
1747  for (int i = 0; i < num_samplelinelogs; i++) {
1749  }
1750  }
1751 
1752  }
1753 
1758  }
1759 
1760  // Create object to do line and plane sampling if needed
1761  bool do_line = false; bool do_plane = false;
1762  pp.queryAdd("do_line_sampling",do_line); pp.queryAdd("do_plane_sampling",do_plane);
1763  if (do_line) {
1764  if (line_sampling_interval < 0 && line_sampling_per < 0) {
1765  Abort("Need to specify line_sampling_interval or line_sampling_per");
1766  }
1767  line_sampler = std::make_unique<LineSampler>();
1768  line_sampler->write_coords(z_phys_cc, geom);
1769  }
1770  if (do_plane) {
1772  Abort("Need to specify plane_sampling_interval or plane_sampling_per");
1773  }
1774  plane_sampler = std::make_unique<PlaneSampler>();
1775  }
1776 
1777  // Create the object that writes station time series if any stations are named
1778  {
1779  // Naming stations turns the output on; an explicit erf.do_station_sampling
1780  // = false turns it back off, so a deck can keep its stations and have the
1781  // output switched off from the command line.
1782  bool do_station = (pp.countval("station_names") > 0);
1783  pp.queryAdd("do_station_sampling", do_station);
1784  if (do_station && pp.countval("station_names") == 0) {
1785  Abort("erf.do_station_sampling is true but erf.station_names is empty, "
1786  "so there is nothing to sample");
1787  }
1788  if (do_station) {
1790  // No default, as for the line and plane samplers above. A
1791  // sample costs a fillpatch and a fill of every requested
1792  // variable over every level that hosts a station, so defaulting
1793  // to every step would make a run with a one-second time step
1794  // pay for output nobody asked for, and write a row a second.
1795  Abort("Need to specify station_sampling_interval or station_sampling_per");
1796  }
1797  station_sampler = std::make_unique<StationSampler>(pp_prefix);
1798  station_sampler->setRestart(!restart_chkfile.empty());
1799  init_stations();
1800  // Start the series at the initial condition, as WRF's tslist does.
1801  // A restart does not repeat it: the row at that time is already in
1802  // the file the earlier run wrote.
1803  if (restart_chkfile.empty()) {
1804  sample_stations(static_cast<Real>(t_new[0]));
1805  }
1806  }
1807  }
1808 
1809  if ( solverChoice.terrain_type == TerrainType::EB ||
1810  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
1811  solverChoice.buildings_type == BuildingsType::ImmersedForcing )
1812  {
1813  bool write_eb_surface = false;
1814  pp.queryAdd("write_eb_surface", write_eb_surface);
1815  if (write_eb_surface) {
1816  if (verbose > 0) {
1817  amrex::Print() << "Writing the geometry to a vtp file.\n" << std::endl;
1818  }
1819  WriteEBSurface(grids[finest_level],dmap[finest_level],Geom(finest_level),&EBFactory(finest_level));
1820  }
1821  }
1822 }
static const std::string BoundaryFaceName[]
Definition: ERF_DataStruct.H:190
const amrex::Real * prob_hi
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:17
constexpr amrex::Real three
Definition: ERF_NumericalConstants.H:32
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
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool wrf_bdy_has_separate_hydrometeors(const MoistureComponentIndices &moisture_indices) noexcept
Definition: ERF_Utils.H:32
void fill_wall_dist_ghost_cells(MultiFab &wdist, const Geometry &geom)
Definition: ERF_Utils.cpp:64
static int last_check_file_step
Definition: ERF.H:1468
amrex::Vector< std::string > samplelinelogname
Definition: ERF.H:2145
void setRayleighRefFromSounding(bool restarting)
Set Rayleigh mean profiles from input sounding.
Definition: ERF_InitRayleigh.cpp:85
amrex::Vector< amrex::IntVect > sampleline
Definition: ERF.H:2146
static double last_plot2d_file_time_1
Definition: ERF.H:1472
amrex::Real plane_sampling_per
Definition: ERF.H:2120
double m_plot3d_per_2
Definition: ERF.H:1544
static int last_plot2d_file_step_2
Definition: ERF.H:1467
int station_sampling_interval
Definition: ERF.H:2125
void setRecordDataInfo(int i, const std::string &filename)
Definition: ERF.H:2043
void init_stations()
Definition: ERF_StationSampler.cpp:684
static bool plot_file_on_restart
Definition: ERF.H:1479
double m_check_per
Definition: ERF.H:1559
static double sum_per
Definition: ERF.H:1690
amrex::Vector< std::string > lsm_flux_name
Definition: ERF.H:1297
void sum_derived_quantities(double time)
Definition: ERF_WriteScalarProfiles.cpp:179
void sample_stations(amrex::Real time)
Definition: ERF_StationSampler.cpp:1301
amrex::Vector< double > last_subvol_time
Definition: ERF.H:1477
amrex::Vector< std::unique_ptr< std::fstream > > samplelinelog
Definition: ERF.H:2144
int m_plot2d_int_2
Definition: ERF.H:1538
int m_plot3d_int_1
Definition: ERF.H:1535
static double last_plot2d_file_time_2
Definition: ERF.H:1473
static int sum_interval
Definition: ERF.H:1688
static int pert_interval
Definition: ERF.H:1689
amrex::Real line_sampling_per
Definition: ERF.H:2119
void restart()
Definition: ERF.cpp:2342
static int last_plot3d_file_step_2
Definition: ERF.H:1465
amrex::Real station_sampling_per
Definition: ERF.H:2126
amrex::Vector< std::string > plot2d_var_names_1
Definition: ERF.H:1565
void setPlotVariables2D(const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile2D.cpp:113
static double last_plot3d_file_time_2
Definition: ERF.H:1471
int profile_int
Definition: ERF.H:1553
bool destag_profiles
Definition: ERF.H:1554
void compute_max_pressure_gradient_diagnostic(int lev)
Definition: ERF_Diagnostics.cpp:14
int m_check_int
Definition: ERF.H:1558
static double last_plot3d_file_time_1
Definition: ERF.H:1470
double m_plot2d_per_2
Definition: ERF.H:1546
void write_1D_profiles(double time)
Definition: ERF_Write1DProfiles.cpp:21
void initRayleigh_at_level(const int &lev)
Initialize Rayleigh damping profiles at a level.
Definition: ERF_InitRayleigh.cpp:16
void appendPlotVariables(const std::string &pp_plot_var_names, amrex::Vector< std::string > &plot_var_names)
Definition: ERF_Plotfile.cpp:183
amrex::Vector< std::string > tot_e_datalogname
Definition: ERF.H:2138
static int output_bndry_planes
Definition: ERF.H:1781
static std::string nc_bdy_file
Definition: ERF.H:1708
void AverageDown()
Definition: ERF_AverageDown.cpp:16
void write_1D_profiles_stag(double time)
Definition: ERF_Write1DProfiles_stag.cpp:29
void Write2DPlotFile(int which, PlotFileType plotfile_type, amrex::Vector< std::string > plot_var_names)
Definition: ERF_Plotfile2D.cpp:645
void compute_max_buoyancy_gradp_diagnostic(int lev)
Definition: ERF_Diagnostics.cpp:307
amrex::Vector< std::string > sampleptlogname
Definition: ERF.H:2141
void setRecordDerDataInfo(int i, const std::string &filename)
Definition: ERF.H:2056
amrex::Vector< std::unique_ptr< std::fstream > > sampleptlog
Definition: ERF.H:2140
void poisson_wall_dist(int lev)
Definition: ERF_PoissonWallDist.cpp:28
static bool is_it_time_for_action(int nstep, double time, double dt, int action_interval, double action_per)
Definition: ERF_WriteScalarProfiles.cpp:663
void sum_integrated_quantities(double time)
Definition: ERF_WriteScalarProfiles.cpp:19
amrex::Vector< double > m_subvol_per
Definition: ERF.H:1541
std::unique_ptr< WriteBndryPlanes > m_w2d
Definition: ERF.H:1844
void init_ibseb()
Definition: ERF_IBSEB.cpp:34
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:21
amrex::Vector< std::string > plot2d_var_names_2
Definition: ERF.H:1566
void WriteSubvolume(int isub, amrex::Vector< std::string > subvol_var_names)
Definition: ERF_WriteSubvolume.cpp:113
void sum_energy_quantities(double time)
Definition: ERF_WriteScalarProfiles.cpp:318
amrex::Vector< std::string > lsm_data_name
Definition: ERF.H:1295
void initSponge()
Initialize sponge profiles.
Definition: ERF_InitSponge.cpp:35
std::unique_ptr< PlaneSampler > plane_sampler
Definition: ERF.H:2122
amrex::Vector< std::unique_ptr< std::fstream > > tot_e_datalog
Definition: ERF.H:2135
double m_plot3d_per_1
Definition: ERF.H:1543
int real_width
Definition: ERF.H:1709
amrex::Vector< int > last_subvol_step
Definition: ERF.H:1476
static PlotFileType plotfile3d_type_2
Definition: ERF.H:1694
void setRecordEnergyDataInfo(int i, const std::string &filename)
Definition: ERF.H:2069
void ResetIntervalMeansAfter3DPlotfileBatch(int plotfiles_written, bool interval_diagnostic_consumed)
Definition: ERF.cpp:344
static PlotFileType plotfile2d_type_2
Definition: ERF.H:1696
int plane_sampling_interval
Definition: ERF.H:2118
int m_plot2d_int_1
Definition: ERF.H:1537
void WriteCheckpointFile() const
Definition: ERF_Checkpoint.cpp:148
bool Write3DPlotFile(int which, PlotFileType plotfile_type, amrex::Vector< std::string > plot_var_names)
Definition: ERF_Plotfile.cpp:2068
static double last_check_file_time
Definition: ERF.H:1474
static std::string nc_low_file
Definition: ERF.H:1713
static int last_plot2d_file_step_1
Definition: ERF.H:1466
void Construct_ERFFillPatchers(int lev)
Definition: ERF.cpp:3547
void setRecordSampleLineInfo(int i, int lev, amrex::IntVect &cell, const std::string &filename)
Definition: ERF.H:2099
void setSpongeRefFromSounding(bool restarting)
Set sponge mean profiles from input sounding.
Definition: ERF_InitSponge.cpp:65
int line_sampling_interval
Definition: ERF.H:2117
void project_initial_velocity(int lev, double time, double dt)
Definition: ERF_PoissonSolve.cpp:72
amrex::Vector< amrex::IntVect > samplepoint
Definition: ERF.H:2142
static int last_plot3d_file_step_1
Definition: ERF.H:1464
std::unique_ptr< LineSampler > line_sampler
Definition: ERF.H:2121
static PlotFileType plotfile2d_type_1
Definition: ERF.H:1695
static PlotFileType plotfile3d_type_1
Definition: ERF.H:1693
amrex::Vector< int > m_subvol_int
Definition: ERF.H:1540
void setRecordSamplePointInfo(int i, int lev, amrex::IntVect &cell, const std::string &filename)
Definition: ERF.H:2082
bool has_surface_layer_inputs(const std::string &prefix) const
Definition: ERF.cpp:3828
int m_plot3d_int_2
Definition: ERF.H:1536
static double bndry_output_planes_start_time
Definition: ERF.H:1784
void ReadCheckpointFileSurfaceLayer()
Definition: ERF_Checkpoint.cpp:1771
double m_plot2d_per_1
Definition: ERF.H:1545
bool initialization_plot_consumes_interval(bool restarting, bool interval_diagnostic_selected) noexcept
Definition: ERF_IntervalMeansCheckpoint.H:186
bool initialization_accumulates_state(bool restarting) noexcept
Definition: ERF_IntervalMeansCheckpoint.H:180
bool plot3d_selection_has_interval_mean_diagnostic(const amrex::Vector< std::string > &names)
Definition: ERF_PlotfileSelection.H:201
bool have_zero_flux_faces
Whether any zero-flux thin immersed-body faces were specified.
Definition: ERF_AdvStruct.H:462
amrex::Real dynamic_viscosity
Dynamic viscosity for momentum diffusion [kg/(m-s)].
Definition: ERF_DiffStruct.H:106
amrex::Real rho0_trans
Reference density used to compute dynamic diffusion coefficients [kg/m3].
Definition: ERF_DiffStruct.H:101
amrex::Real qv_ref_inp_sound
Surface reference water vapor mixing ratio read from the input sounding [kg/kg].
Definition: ERF_InputSoundingData.H:503
amrex::Real theta_ref_inp_sound
Surface reference potential temperature read from the input sounding [K].
Definition: ERF_InputSoundingData.H:503
void read_forcing_file()
Read the forcing file and cache the raw time slices on host.
Definition: ERF_LargeScaleForcingData.H:64
bool verbose_print
Definition: ERF_LargeScaleForcingData.H:416
void interp_forcing(const amrex::GeometryData &geom, const amrex::Vector< amrex::Real > &zlevels_stag, const InputSoundingData &sounding)
Interpolate the forcing profiles onto the model grid.
Definition: ERF_LargeScaleForcingData.H:217
std::string abl_geo_wind_table
Path to a time-varying geostrophic wind table.
Definition: ERF_DataStruct.H:2255
bool forest_biophysics
Definition: ERF_DataStruct.H:2299
bool use_rotate_surface_flux
Whether MOST surface fluxes are rotated with terrain.
Definition: ERF_DataStruct.H:2105
bool any_perturbation() const
Query whether any level uses a turbulent perturbation mode.
Definition: ERF_DataStruct.H:2217
bool spatial_moisture_forcing
Whether spatially varying moisture forcing is enabled.
Definition: ERF_DataStruct.H:2083
bool have_geo_wind_profile
Whether a geostrophic wind profile has been configured.
Definition: ERF_DataStruct.H:2256
bool rebalance_wrf_input
Whether to rebalance states from wrfinput and wrfbdy data.
Definition: ERF_DataStruct.H:2057
bool do_forest_drag
Whether forest canopy drag is enabled.
Definition: ERF_DataStruct.H:2296
bool forest_biophysics_heat
Definition: ERF_DataStruct.H:2300
bool spatial_rhotheta_forcing
Whether spatially varying rho-theta forcing is enabled.
Definition: ERF_DataStruct.H:2082
bool use_wrf_bdy_qc_qi
Whether WRF cloud water and active cloud ice are ingested at real boundaries.
Definition: ERF_DataStruct.H:2281
void debug(double)
Write perturbation debug information when debug output is enabled.
Definition: ERF_TurbPertStruct.H:736
Here is the call graph for this function:

◆ InitData_pre()

void ERF::InitData_pre ( )
626 {
628  amrex::Print() << "ERF InitData_pre: driver-managed atm2ocn coupling enabled"
629  << " two_way=" << (m_driver_uses_two_way_coupling ? 1 : 0)
630  << " active_contract="
631  << (m_driver_atm2ocn_uses_state_contract ? "state" : "flux")
632  << "\n";
633  }
634 
635  // Initialize the start time for our CPU-time tracker
636  startCPUTime = ParallelDescriptor::second();
637 
638  // Create the ReadBndryPlanes object so we can read boundary plane data
639  // m_r2d is used by init_bcs so we must instantiate this class before
640  if (input_bndry_planes) {
641  Print() << "Defining r2d for the first time " << std::endl;
642  m_r2d = std::make_unique<ReadBndryPlanes>(geom[0], solverChoice.rdOcp);
643  }
644 
645  if (restart_chkfile.empty()) {
646  // Start simulation from the beginning
647  InitFromScratch(zero);
648  } else {
649  // For initialization this is done in init_only; it is done here for restart
650  init_bcs();
651  }
652 
653  solverChoice.check_params(max_level,geom,phys_bc_type);
654 }
void init_bcs()
Definition: ERF_InitBCs.cpp:321
void check_params(int max_level, const amrex::Vector< amrex::Geometry > &geom_vect, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > phys_bc_type)
Validate solver options after geometry and boundary conditions are known.
Definition: ERF_DataStruct.H:1602

◆ initHSE() [1/2]

void ERF::initHSE ( )
private

Initialize HSE.

Initialize density and pressure base state on every active AMR level.

209 {
210  for (int lev = 0; lev <= finest_level; lev++)
211  {
212  initHSE(lev);
213  }
214 }

◆ initHSE() [2/2]

void ERF::initHSE ( int  lev)
private

Initialize density and pressure base state in hydrostatic equilibrium.

Parameters
levInteger specifying the current level
23 {
24  // This integrates up through column to update p_hse, pi_hse, th_hse;
25  // r_hse is not const b/c FillBoundary is called at the end for r_hse and p_hse
26 
27  MultiFab r_hse (base_state[lev], make_alias, BaseState::r0_comp, 1);
28  MultiFab p_hse (base_state[lev], make_alias, BaseState::p0_comp, 1);
29  MultiFab pi_hse(base_state[lev], make_alias, BaseState::pi0_comp, 1);
30  MultiFab th_hse(base_state[lev], make_alias, BaseState::th0_comp, 1);
31  MultiFab qv_hse(base_state[lev], make_alias, BaseState::qv0_comp, 1);
32 
33  bool all_boxes_touch_bottom = true;
34  Box domain(geom[lev].Domain());
35 
36  if (lev == 0) {
37  BoxArray ba(base_state[lev].boxArray());
38  for (int i = 0; i < ba.size(); i++) {
39  if (ba[i].smallEnd(2) != domain.smallEnd(2)) {
40  all_boxes_touch_bottom = false;
41  }
42  }
43  }
44  else
45  {
46  //
47  // Set the values of the base state inside the domain but outside of the fine region,
48  // and in the fine ghost cells; the vertical integration below then overwrites only
49  // the fine grids, and for a box whose klo is in the interior it reads the value this
50  // leaves in klo-1 as its starting point.
51  //
53  }
54 
55  bool is_constant_dz = (solverChoice.mesh_type == MeshType::ConstantDz);
56  bool is_stretched_dz = (solverChoice.mesh_type == MeshType::StretchedDz);
57 
58  auto initialize_base_state =
59  [this, lev, is_constant_dz, is_stretched_dz]
60  (MultiFab& dens, MultiFab& pres, MultiFab& pi, MultiFab& theta, MultiFab& qv,
61  std::unique_ptr<MultiFab>& z_phys_nd_in, std::unique_ptr<MultiFab>& z_phys_cc_in)
62  {
63  if (solverChoice.init_type == InitType::MoistBaseState ||
64  solverChoice.init_type == InitType::HindCast)
65  {
66  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type == MeshType::ConstantDz);
67  prob->erf_init_dens_hse_moist(dens, z_phys_nd_in, geom[lev]);
68  }
69  else if (solverChoice.init_type == InitType::ConstantDensity)
70  {
71  prob->erf_init_const_dens_hse(dens);
72  }
73  else if (solverChoice.init_type == InitType::Uniform)
74  {
76  prob->erf_init_const_dens_and_th_hse(dens, pres, pi, theta, qv, solverChoice.rdOcp);
77  }
78  else if (solverChoice.init_type == InitType::ConstantDensityLinearTheta)
79  {
81  prob->erf_init_const_dens_and_linear_th_hse(dens, pres, pi, theta, qv,
82  solverChoice.rdOcp, z_phys_cc_in);
83  }
84  else
85  {
86  prob->erf_init_dens_hse_dry(dens, z_phys_nd_in, z_phys_cc_in, geom[lev],
87  stretched_dz_h[lev], is_constant_dz, is_stretched_dz);
88  }
89 
90  if (solverChoice.init_type != InitType::Uniform &&
91  solverChoice.init_type != InitType::ConstantDensityLinearTheta)
92  {
93  erf_enforce_hse(lev, dens, pres, pi, theta, qv, z_phys_cc_in);
94  }
95  };
96 
97  if (all_boxes_touch_bottom || lev > 0) {
98 
99  initialize_base_state(r_hse, p_hse, pi_hse, th_hse, qv_hse,
100  z_phys_nd[lev], z_phys_cc[lev]);
101 
102  //
103  // Impose physical bc's on the base state
104  //
105  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
106 
107  } else {
108 
109  BoxArray ba_new(domain);
110 
111  ChopGrids2D(ba_new, domain, ParallelDescriptor::NProcs());
112 
113  DistributionMapping dm_new(ba_new);
114 
115  MultiFab new_base_state(ba_new, dm_new, BaseState::num_comps, base_state[lev].nGrowVect());
116  new_base_state.ParallelCopy(base_state[lev],0,0,base_state[lev].nComp(),
117  base_state[lev].nGrowVect(),base_state[lev].nGrowVect());
118 
119  MultiFab new_r_hse (new_base_state, make_alias, BaseState::r0_comp, 1);
120  MultiFab new_p_hse (new_base_state, make_alias, BaseState::p0_comp, 1);
121  MultiFab new_pi_hse(new_base_state, make_alias, BaseState::pi0_comp, 1);
122  MultiFab new_th_hse(new_base_state, make_alias, BaseState::th0_comp, 1);
123  MultiFab new_qv_hse(new_base_state, make_alias, BaseState::qv0_comp, 1);
124 
125  std::unique_ptr<MultiFab> new_z_phys_cc;
126  std::unique_ptr<MultiFab> new_z_phys_nd;
127  if (solverChoice.mesh_type != MeshType::ConstantDz ||
128  solverChoice.init_type == InitType::ConstantDensityLinearTheta)
129  {
130  new_z_phys_cc = std::make_unique<MultiFab>(ba_new,dm_new,1,1);
131  new_z_phys_cc->ParallelCopy(*z_phys_cc[lev],0,0,1,1,1);
132  }
133 
134  if (solverChoice.mesh_type != MeshType::ConstantDz) {
135  BoxArray ba_new_nd(ba_new);
136  ba_new_nd.surroundingNodes();
137  new_z_phys_nd = std::make_unique<MultiFab>(ba_new_nd,dm_new,1,1);
138  new_z_phys_nd->ParallelCopy(*z_phys_nd[lev],0,0,1,1,1);
139  }
140 
141  initialize_base_state(new_r_hse, new_p_hse, new_pi_hse, new_th_hse, new_qv_hse,
142  new_z_phys_nd, new_z_phys_cc);
143 
144  //
145  // Impose physical bc's on the base state (we must make new, temporary bcs object because the z_phys_nd is different)
146  //
147  ERFPhysBCFunct_base* temp_physbcs_base =
148  new ERFPhysBCFunct_base(lev, geom[lev], domain_bcs_type, domain_bcs_type_d, new_z_phys_nd,
149  (solverChoice.terrain_type == TerrainType::MovingFittedMesh),
150  (solverChoice.mesh_type != MeshType::ConstantDz),
152  (*temp_physbcs_base)(new_base_state,0,new_base_state.nComp(),new_base_state.nGrowVect());
153  delete temp_physbcs_base;
154 
155  // Now copy back into the original arrays
156  base_state[lev].ParallelCopy(new_base_state,0,0,base_state[lev].nComp(),
157  base_state[lev].nGrowVect(),base_state[lev].nGrowVect());
158  }
159 
160  //
161  // Impose physical bc's on the base state -- the values outside the fine region
162  // but inside the domain have already been filled in the call above to InterpFromCoarseLevel
163  //
164  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
165 }
void ChopGrids2D(BoxArray &ba, const Box &domain, int target_size)
Definition: ERF_ChopGrids.cpp:35
Definition: ERF_PhysBCFunct.H:469
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:228
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
1283 {
1284  const BoxArray& ba(cons_mf.boxArray());
1285  const DistributionMapping& dm(cons_mf.DistributionMap());
1286 
1287  int ncomp_cons = cons_mf.nComp();
1288 
1289  // Initialize the integrator memory
1290  Vector<MultiFab> int_state; // integration state data structure example
1291  int_state.push_back(MultiFab(cons_mf, make_alias, 0, ncomp_cons)); // cons
1292  int_state.push_back(MultiFab(convert(ba,IntVect(1,0,0)), dm, 1, vel_mf.nGrow())); // xmom
1293  int_state.push_back(MultiFab(convert(ba,IntVect(0,1,0)), dm, 1, vel_mf.nGrow())); // ymom
1294  int_state.push_back(MultiFab(convert(ba,IntVect(0,0,1)), dm, 1, vel_mf.nGrow())); // zmom
1295 
1296  mri_integrator_mem[lev] = std::make_unique<MRISplitIntegrator<Vector<MultiFab> > >(int_state);
1297  mri_integrator_mem[lev]->setNoSubstepping((solverChoice.substepping_type[lev] == SubsteppingType::None));
1298  mri_integrator_mem[lev]->setAnelastic(solverChoice.anelastic[lev]);
1299  mri_integrator_mem[lev]->setAnelasticType(solverChoice.anelastic_type[lev]);
1300  mri_integrator_mem[lev]->setNcompCons(ncomp_cons);
1301  mri_integrator_mem[lev]->setForceFirstStageSingleSubstep(solverChoice.force_stage1_single_substep);
1302 }
amrex::Vector< AnelasticType > anelastic_type
Per-level two-stage scheme used by the anelastic integrator.
Definition: ERF_DataStruct.H:1982

◆ initializeEB()

void ERF::initializeEB ( )
private
24 {
25  //
26  // Construct the EB data structures and store in a separate class
27  //
28  std::string geometry ="terrain";
29  ParmParse pp_eb2("eb2");
30  pp_eb2.queryAdd("geometry", geometry);
31  if ( solverChoice.terrain_type == TerrainType::EB ||
32  solverChoice.terrain_type == TerrainType::ImmersedForcing)
33  {
34  constexpr int ngrow_for_eb = 4; // This is the default in amrex but we need to explicitly pass it here since
35  // we want to also pass the build_coarse_level_by_coarsening argument
36  const bool build_eb_for_multigrid = (solverChoice.terrain_type == TerrainType::EB &&
38  solverChoice.anelastic[0] == 1));
39  // Note this just needs to be an integer > number of V-cycles one might use
40  const int max_coarsening_level = (build_eb_for_multigrid) ? 100 : 0;
41  const bool build_coarse_level_by_coarsening(false);
42 
43  // Define GeometryShop using the implicit function
44  if (geometry == "terrain") {
45  // Query building STL parameters upfront
46  //
47  // Note these live under the "erf" prefix, not "eb2", because "eb2" is amrex's own
48  // namespace and amrex already reads eb2.stl_* for its own eb2.geometry = stl path.
49  ParmParse pp_erf("erf");
50  std::string buildings_stl_file;
51  bool has_buildings_stl = pp_erf.query("buildings_stl_file", buildings_stl_file);
52 
53  Real stl_scale = 1.0;
54  Array<Real,3> stl_center = {zero, zero, zero};
55  int stl_reverse_normal = 0;
56 
57  if (has_buildings_stl) {
58  pp_erf.query("buildings_stl_scale", stl_scale);
59  pp_erf.query("buildings_stl_center", stl_center);
60  pp_erf.query("buildings_stl_reverse_normal", stl_reverse_normal);
61  }
62 
63  // Decide whether the terrain surface is part of the implicit function. By default
64  // we include it whenever the user has specified terrain by any of the means that
65  // init_terrain_surface understands -- not just erf.terrain_file_name -- but the
66  // user may also state the choice outright with erf.buildings_only.
67  bool buildings_only = (has_buildings_stl && !prob->terrain_is_specified());
68 
69  bool buildings_only_in = false;
70  if (pp_erf.query("buildings_only", buildings_only_in)) {
71  if (buildings_only_in && !has_buildings_stl) {
72  Abort("erf.buildings_only is true but no erf.buildings_stl_file was given");
73  }
74  buildings_only = buildings_only_in;
75  }
76 
77  // Determine geometry mode and log configuration
78  std::string mode_description;
79  if (buildings_only) {
80  mode_description = "Buildings-only mode (no terrain)";
81  } else if (has_buildings_stl) {
82  mode_description = "Terrain + 3D buildings";
83  } else {
84  mode_description = "Terrain-only mode (no buildings)";
85  }
86 
87  Print() << "Building EB geometry: " << mode_description << "\n";
88  if (has_buildings_stl) {
89  Print() << " STL file: " << buildings_stl_file << "\n";
90  Print() << " STL scale: " << stl_scale << "\n";
91  Print() << " STL center: " << stl_center[0] << " " << stl_center[1] << " " << stl_center[2] << "\n";
92  Print() << " Reverse normals: " << stl_reverse_normal << "\n";
93  }
94 
95  // Lambda to build EB geometry from shop
96  auto build_eb = [&](auto const& gshop) {
97  if (build_eb_for_multigrid) {
98  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
99  ngrow_for_eb, build_coarse_level_by_coarsening);
100  } else {
101  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
102 #if USE_FC_FACTORY
103  EB2::BuildFC();
104 #endif
105  }
106  };
107 
108  // Build the appropriate implicit function and geometry
109  if (buildings_only) {
110  // Buildings-only
111  BuildingsIF buildings_if(buildings_stl_file, stl_scale, stl_center,
112  stl_reverse_normal, geom[max_level]);
113  auto gshop = EB2::makeShop(buildings_if);
114  build_eb(gshop);
115 
116  } else {
117  // Load terrain (from file or custom init_my_custom_terrain)
118  Box terrain_bx(surroundingNodes(geom[max_level].Domain()));
119  terrain_bx.grow(3);
120  FArrayBox terrain_fab(makeSlab(terrain_bx,2,0),1);
121  double dummy_time = 0.0;
122  prob->init_terrain_surface(geom[max_level], terrain_fab, dummy_time);
123  TerrainIF terrain_if(terrain_fab, geom[max_level], stretched_dz_d[max_level]);
124 
125  if (has_buildings_stl) {
126  // Terrain + Buildings
127  BuildingsIF buildings_if(buildings_stl_file, stl_scale, stl_center,
128  stl_reverse_normal, geom[max_level]);
129  auto combined_if = EB2::makeUnion(terrain_if, buildings_if);
130  auto gshop = EB2::makeShop(combined_if);
131  build_eb(gshop);
132  } else {
133  // Terrain-only
134  auto gshop = EB2::makeShop(terrain_if);
135  build_eb(gshop);
136  }
137  }
138 
139  Print() << "EB geometry built successfully: " << mode_description << ".\n";
140  } else if (geometry == "plane") {
141  RealArray plane_point{zero, zero, zero};
142  RealArray plane_normal{zero, zero, -one}; // pointing into the solid region
143  pp_eb2.queryAdd("plane_point", plane_point);
144  pp_eb2.queryAdd("plane_normal", plane_normal);
145  EB2::PlaneIF implicit_fun(plane_point, plane_normal, true);
146  auto gshop = EB2::makeShop(implicit_fun);
147  if (build_eb_for_multigrid) {
148  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
149  ngrow_for_eb, build_coarse_level_by_coarsening);
150  } else {
151  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
152 #if USE_FC_FACTORY
153  EB2::BuildFC();
154 #endif
155  }
156  } else if (geometry == "box") {
157  RealArray box_lo{zero, zero, zero};
158  RealArray box_hi{zero, zero, zero};
159  pp_eb2.queryAdd("box_lo", box_lo);
160  pp_eb2.queryAdd("box_hi", box_hi);
161  EB2::BoxIF implicit_fun(box_lo, box_hi, false);
162  auto gshop = EB2::makeShop(implicit_fun);
163  if (build_eb_for_multigrid) {
164  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
165  ngrow_for_eb, build_coarse_level_by_coarsening);
166  } else {
167  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
168 #if USE_FC_FACTORY
169  EB2::BuildFC();
170 #endif
171  }
172  } else if (geometry == "sphere") {
173  auto ProbLoArr = geom[max_level].ProbLoArray();
174  auto ProbHiArr = geom[max_level].ProbHiArray();
175  const Real xcen = myhalf * (ProbLoArr[0] + ProbHiArr[0]);
176  const Real ycen = myhalf * (ProbLoArr[1] + ProbHiArr[1]);
177  RealArray sphere_center = {xcen, ycen, zero};
178  EB2::SphereIF implicit_fun(myhalf, sphere_center, false);
179  auto gshop = EB2::makeShop(implicit_fun);
180  if (build_eb_for_multigrid) {
181  EB2::Build(gshop, geom[max_level], max_level, max_coarsening_level,
182  ngrow_for_eb, build_coarse_level_by_coarsening);
183  } else {
184  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
185 #if USE_FC_FACTORY
186  EB2::BuildFC();
187 #endif
188  }
189  }
190  }
191 
192  if ( solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
193  constexpr int ngrow_for_eb = 4;
194  if (geometry == "terrain") {
195  Box buildings_bx(surroundingNodes(geom[max_level].Domain())); buildings_bx.grow(3);
196  FArrayBox buildings_fab(makeSlab(buildings_bx,2,0),1);
197  double dummy_time = 0.0;
198  prob->init_buildings_surface(geom[max_level], buildings_fab, dummy_time);
199  TerrainIF implicit_fun(buildings_fab, geom[max_level], stretched_dz_d[max_level]);
200  auto gshop = EB2::makeShop(implicit_fun);
201  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
202 #if USE_FC_FACTORY
203  EB2::BuildFC();
204 #endif
205  } else if (geometry == "plane") {
206  amrex::Abort("plane geometry is not supported with ImmersedForcing for buildings");
207  } else if (geometry == "box") {
208  RealArray box_lo{zero, zero, zero};
209  RealArray box_hi{zero, zero, zero};
210  pp_eb2.queryAdd("box_lo", box_lo);
211  pp_eb2.queryAdd("box_hi", box_hi);
212  EB2::BoxIF implicit_fun(box_lo, box_hi, false);
213  auto gshop = EB2::makeShop(implicit_fun);
214  EB2::Build(gshop, this->Geom(), ngrow_for_eb);
215 #if USE_FC_FACTORY
216  EB2::BuildFC();
217 #endif
218  } else if (geometry == "sphere") {
219  amrex::Abort("sphere geometry is not supported with ImmersedForcing for buildings");
220  }
221  }
222 }
Buildings implicit function backed by STL triangle mesh.
Definition: ERF_EBIFBuildings.H:42
AMReX EB implicit-function sign convention: >0 is body, 0 is boundary, <0 is fluid.
Definition: ERF_EBIFTerrain.H:24

Referenced by ERF_shared().

Here is the caller graph for this function:

◆ 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
2309 {
2310  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Eulerian) {
2311 
2312  micro = std::make_unique<EulerianMicrophysics>(a_nlevsmax, solverChoice.moisture_type);
2313 
2314  } else if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
2315 #ifdef ERF_USE_PARTICLES
2316  micro = std::make_unique<LagrangianMicrophysics>(a_nlevsmax, solverChoice.moisture_type);
2317  /* Lagrangian microphysics models will have a particle container; it needs to be added
2318  to ERF::particleData */
2319  const auto& pc_name( dynamic_cast<LagrangianMicrophysics&>(*micro).getName() );
2320  /* The particle container has not yet been constructed and initialized, so just add
2321  its name here for now (so that functions to set plotting variables can see it). */
2322  particleData.addName( pc_name );
2323 
2324 #else
2325  Abort("Lagrangian microphysics can be used when compiled with ERF_USE_PARTICLES");
2326 #endif
2327  }
2328 
2329  qmoist.resize(a_nlevsmax);
2330  return;
2331 }

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.

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

◆ 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:1816

◆ 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:1158
std::string input_sponge_file
Path to the text file containing the sponge profile.
Definition: ERF_InputSpongeData.H:120
void read_from_file(const amrex::Geometry &geom, const amrex::Vector< amrex::Real > &zlevels_stag)
Read and interpolate the input sponge profile onto level-0 heights.
Definition: ERF_InputSpongeData.H:36

◆ Interp2DArrays()

void ERF::Interp2DArrays ( int  lev,
const amrex::BoxArray &  my_ba2d,
const amrex::DistributionMapping &  my_dm 
)
2069 {
2070  if (lev == 0) { return; }
2071 
2072  //
2073  // These fields have no vertical extent: ba2d collapses z to a single index at both
2074  // levels, so there is nothing in the vertical for the interpolation to do. It must
2075  // therefore be handed a ratio of 1 in z even when the levels really are refined in
2076  // the vertical -- a ratio greater than 1 makes the interpolator ask for a layer of
2077  // coarse ghost cells in z that a 2-D MultiFab does not, and cannot, carry.
2078  //
2079  const IntVect rr2d(refRatio(lev-1)[0], refRatio(lev-1)[1], 1);
2080 
2081  //
2082  // Map factors, surface pressure, land mask and land / soil types. Unlike the fields
2083  // below, these always exist by the time we get here (init_stuff builds them), so there
2084  // is no "only if it hasn't been made yet" guard -- we must overwrite the placeholder
2085  // that init_stuff left behind (a map factor of 1, a uniform land mask, an
2086  // uninitialized surface pressure). A level that read its own init file already has
2087  // all of them from that file and must be left alone.
2088  //
2089  if (!has_own_init_file(lev)) {
2093  }
2094 
2095  if (lon_m[lev-1] && !lon_m[lev]) {
2096  auto ngv = lon_m[lev-1]->nGrowVect(); ngv[2] = 0;
2097  lon_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
2098  InterpFromCoarseLevel(*lon_m[lev], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
2099  *lon_m[lev-1], 0, 0, 1,
2100  geom[lev-1], geom[lev],
2101  rr2d, &cell_cons_interp,
2103  }
2104  if (lat_m[lev-1] && !lat_m[lev]) {
2105  auto ngv = lat_m[lev-1]->nGrowVect(); ngv[2] = 0;
2106  lat_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
2107  InterpFromCoarseLevel(*lat_m[lev], ngv, IntVect(0,0,0), // do not fill ghost cells outside the domain
2108  *lat_m[lev-1], 0, 0, 1,
2109  geom[lev-1], geom[lev],
2110  rr2d, &cell_cons_interp,
2112  }
2113  //
2114  // The Coriolis factors. Unlike lat_m/lon_m above, these are read one cell past the
2115  // hi domain faces: the momentum sources average them onto the faces of the nodal
2116  // tilebox (see the var_coriolis branch of ERF_MakeMomSources.cpp), so the x-momentum
2117  // source at i = nx reads sinPhi/cosPhi at i = nx. A level that reads its own
2118  // wrfinput/met_em file fills those ghosts by clamping into the valid region; a level
2119  // created by refinement must get them from the parent instead, or the Coriolis source
2120  // on its hi faces is computed from uninitialized memory.
2121  //
2122  // NOTE: filling ghost cells outside the domain requires that ALL ghost cells of the
2123  // coarse MultiFab have already been filled, inside the domain and out. That
2124  // holds here: init_from_wrfinput/init_from_metgrid fill the grown box at a level
2125  // with a file, and this interpolation fills it at a level without one.
2126  //
2127  if (sinPhi_m[lev-1] && !sinPhi_m[lev]) {
2128  auto ngv = sinPhi_m[lev-1]->nGrowVect(); ngv[2] = 0;
2129  sinPhi_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
2130  InterpFromCoarseLevel(*sinPhi_m[lev], ngv, ngv, // DO fill ghost cells outside the domain
2131  *sinPhi_m[lev-1], 0, 0, 1,
2132  geom[lev-1], geom[lev],
2133  rr2d, &cell_cons_interp,
2135  }
2136  if (cosPhi_m[lev-1] && !cosPhi_m[lev]) {
2137  auto ngv = cosPhi_m[lev-1]->nGrowVect(); ngv[2] = 0;
2138  cosPhi_m[lev] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
2139  InterpFromCoarseLevel(*cosPhi_m[lev], ngv, ngv, // DO fill ghost cells outside the domain
2140  *cosPhi_m[lev-1], 0, 0, 1,
2141  geom[lev-1], geom[lev],
2142  rr2d, &cell_cons_interp,
2144  }
2145  if (sst_lev[lev-1][0]) {
2146  if (sst_lev[lev].size() < sst_lev[lev-1].size()) {
2147  sst_lev[lev].resize(sst_lev[lev-1].size());
2148  }
2149 #ifdef ERF_USE_NETCDF
2150  double time_since_start_low = t_new[0] + start_time - start_low_time;
2151  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
2152  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(sst_lev[lev-1].size()));
2153 #else
2154  // TODO: Fix if SST is provided without NETCDF
2155  int n_time_old = 0;
2156  int ntimes_to_interp = 1;
2157 #endif
2158  auto ngv = sst_lev[lev-1][0]->nGrowVect(); ngv[2] = 0;
2159 
2160  for (int n = n_time_old; n < ntimes_to_interp; n++) {
2161  if (!sst_lev[lev-1][n]) { continue; }
2162  if (!sst_lev[lev][n]) {
2163  sst_lev[lev][n] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
2164  // DO fill the ghost cells outside the domain: SurfaceLayer populates t_surf
2165  // on grown tileboxes, so it reads the sea surface temperature past a
2166  // non-periodic domain face. As for the Coriolis factors above, the coarse
2167  // level has those ghost cells filled (update_sst_tsk fills the grown box).
2168  InterpFromCoarseLevel(*sst_lev[lev][n], ngv, ngv,
2169  *sst_lev[lev-1][n], 0, 0, 1,
2170  geom[lev-1], geom[lev],
2171  rr2d, &cell_cons_interp,
2173  }
2174  }
2175  }
2176  if (tsk_lev[lev-1][0]) {
2177  if (tsk_lev[lev].size() < tsk_lev[lev-1].size()) {
2178  tsk_lev[lev].resize(tsk_lev[lev-1].size());
2179  }
2180 #ifdef ERF_USE_NETCDF
2181  double time_since_start_low = t_new[0] + start_time - start_low_time;
2182  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
2183  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(tsk_lev[lev-1].size()));
2184 #else
2185  // TODO: Fix if TSK is provided without NETCDF
2186  int n_time_old = 0;
2187  int ntimes_to_interp = 1;
2188 #endif
2189  auto ngv = tsk_lev[lev-1][0]->nGrowVect(); ngv[2] = 0;
2190 
2191  for (int n = n_time_old; n < ntimes_to_interp; n++) {
2192  if (!tsk_lev[lev-1][n]) { continue; }
2193  if (!tsk_lev[lev][n]) {
2194  tsk_lev[lev][n] = std::make_unique<MultiFab>(my_ba2d,my_dm,1,ngv);
2195  // DO fill the ghost cells outside the domain -- see the note on sst_lev
2196  InterpFromCoarseLevel(*tsk_lev[lev][n], ngv, ngv,
2197  *tsk_lev[lev-1][n], 0, 0, 1,
2198  geom[lev-1], geom[lev],
2199  rr2d, &cell_cons_interp,
2201  }
2202  }
2203  }
2204 
2205  Real time_for_fp = 0.0; // This is not actually used
2206  Vector<Real> ftime = {static_cast<Real>(time_for_fp), static_cast<Real>(time_for_fp)};
2207  Vector<Real> ctime = {static_cast<Real>(time_for_fp), static_cast<Real>(time_for_fp)};
2208  if (lat_m[lev]) {
2209  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
2210  Vector<MultiFab*> fmf = {lat_m[lev ].get(), lat_m[lev ].get()};
2211  Vector<MultiFab*> cmf = {lat_m[lev-1].get(), lat_m[lev-1].get()};
2212  IntVect ngv = lat_m[lev]->nGrowVect(); ngv[2] = 0;
2213  Interpolater* mapper = &cell_cons_interp;
2214  FillPatchTwoLevels(*lat_m[lev].get(), ngv, IntVect(0,0,0),
2215  time_for_fp, cmf, ctime, fmf, ftime,
2216  0, 0, 1, geom[lev-1], geom[lev],
2217  rr2d, mapper, domain_bcs_type,
2218  BCVars::cons_bc);
2219  }
2220  if (lon_m[lev]) {
2221  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
2222  Vector<MultiFab*> fmf = {lon_m[lev ].get(), lon_m[lev ].get()};
2223  Vector<MultiFab*> cmf = {lon_m[lev-1].get(), lon_m[lev-1].get()};
2224  IntVect ngv = lon_m[lev]->nGrowVect(); ngv[2] = 0;
2225  Interpolater* mapper = &cell_cons_interp;
2226  FillPatchTwoLevels(*lon_m[lev].get(), ngv, IntVect(0,0,0),
2227  time_for_fp, cmf, ctime, fmf, ftime,
2228  0, 0, 1, geom[lev-1], geom[lev],
2229  rr2d, mapper, domain_bcs_type,
2230  BCVars::cons_bc);
2231  } // lon_m
2232  if (sinPhi_m[lev]) {
2233  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
2234  Vector<MultiFab*> fmf = {sinPhi_m[lev ].get(), sinPhi_m[lev ].get()};
2235  Vector<MultiFab*> cmf = {sinPhi_m[lev-1].get(), sinPhi_m[lev-1].get()};
2236  IntVect ngv = sinPhi_m[lev]->nGrowVect(); ngv[2] = 0;
2237  Interpolater* mapper = &cell_cons_interp;
2238  FillPatchTwoLevels(*sinPhi_m[lev].get(), ngv, IntVect(0,0,0),
2239  time_for_fp, cmf, ctime, fmf, ftime,
2240  0, 0, 1, geom[lev-1], geom[lev],
2241  rr2d, mapper, domain_bcs_type,
2242  BCVars::cons_bc);
2243  } // sinPhi
2244  if (cosPhi_m[lev]) {
2245  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
2246  Vector<MultiFab*> fmf = {cosPhi_m[lev ].get(), cosPhi_m[lev ].get()};
2247  Vector<MultiFab*> cmf = {cosPhi_m[lev-1].get(), cosPhi_m[lev-1].get()};
2248  IntVect ngv = cosPhi_m[lev]->nGrowVect(); ngv[2] = 0;
2249  Interpolater* mapper = &cell_cons_interp;
2250  FillPatchTwoLevels(*cosPhi_m[lev].get(), ngv, IntVect(0,0,0),
2251  time_for_fp, cmf, ctime, fmf, ftime,
2252  0, 0, 1, geom[lev-1], geom[lev],
2253  rr2d, mapper, domain_bcs_type,
2254  BCVars::cons_bc);
2255  } // cosPhi
2256  if (sst_lev[lev][0]) {
2257  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
2258 #ifdef ERF_USE_NETCDF
2259  double time_since_start_low = t_new[0] + start_time - start_low_time;
2260  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
2261  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(sst_lev[lev-1].size()));
2262 #else
2263  // TODO: Fix if SST is provided without NETCDF
2264  int n_time_old = 0;
2265  int ntimes_to_interp = 1;
2266 #endif
2267  for (int n = n_time_old; n < ntimes_to_interp; n++) {
2268  if (!sst_lev[lev][n] || !sst_lev[lev-1][n]) { continue; }
2269  Vector<MultiFab*> fmf = {sst_lev[lev ][n].get(), sst_lev[lev ][n].get()};
2270  Vector<MultiFab*> cmf = {sst_lev[lev-1][n].get(), sst_lev[lev-1][n].get()};
2271  IntVect ngv = sst_lev[lev][n]->nGrowVect(); ngv[2] = 0;
2272  Interpolater* mapper = &cell_cons_interp;
2273  FillPatchTwoLevels(*sst_lev[lev][n].get(), ngv, IntVect(0,0,0),
2274  time_for_fp, cmf, ctime, fmf, ftime,
2275  0, 0, 1, geom[lev-1], geom[lev],
2276  rr2d, mapper, domain_bcs_type,
2277  BCVars::cons_bc);
2278  } // ntimes
2279  } // sst_lev
2280  if (tsk_lev[lev][0]) {
2281  // Call FillPatchTwoLevels which ASSUMES that all ghost cells at lev-1 have already been filled
2282 #ifdef ERF_USE_NETCDF
2283  double time_since_start_low = t_new[0] + start_time - start_low_time;
2284  int n_time_old = static_cast<int>(time_since_start_low / low_time_interval);
2285  int ntimes_to_interp = std::min(n_time_old+3, static_cast<int>(tsk_lev[lev-1].size()));
2286 #else
2287  // TODO: Fix if TSK is provided without NETCDF
2288  int n_time_old = 0;
2289  int ntimes_to_interp = 1;
2290 #endif
2291  for (int n = n_time_old; n < ntimes_to_interp; n++) {
2292  if (!tsk_lev[lev][n] || !tsk_lev[lev-1][n]) { continue; }
2293  Vector<MultiFab*> fmf = {tsk_lev[lev ][n].get(), tsk_lev[lev ][n].get()};
2294  Vector<MultiFab*> cmf = {tsk_lev[lev-1][n].get(), tsk_lev[lev-1][n].get()};
2295  IntVect ngv = tsk_lev[lev][n]->nGrowVect(); ngv[2] = 0;
2296  Interpolater* mapper = &cell_cons_interp;
2297  FillPatchTwoLevels(*tsk_lev[lev][n].get(), ngv, IntVect(0,0,0),
2298  time_for_fp, cmf, ctime, fmf, ftime,
2299  0, 0, 1, geom[lev-1], geom[lev],
2300  rr2d, mapper, domain_bcs_type,
2301  BCVars::cons_bc);
2302  } // ntimes
2303  } // tsk_lev
2304 }
void interp_psfc_from_coarse(int lev)
Definition: ERF.cpp:1921
bool has_own_init_file(int lev) const
Definition: ERF.cpp:1833
void interp_land_masks_from_coarse(int lev)
Definition: ERF.cpp:1978
void interp_mapfac_from_coarse(int lev)
Definition: ERF.cpp:1856
Here is the call graph for this function:

◆ interp_base_state_from_coarse()

void ERF::interp_base_state_from_coarse ( int  lev)
private

Fill base_state[lev] by conservative interpolation from the next coarser level and then apply the base-state physical boundary conditions.

This is what gives values to the part of a fine level that lies inside the domain but outside the fine grids, and to the fine ghost cells. Any construction that writes only the fine grids – initHSE's vertical integration, or the analytic WRF profile – must run after this, not before.

Parameters
levInteger specifying the current level; must be > 0
180 {
181  AMREX_ALWAYS_ASSERT(lev > 0);
182 
183  int icomp = 0; int ncomp = BaseState::num_comps;
184 
185  base_state[lev-1].FillBoundary(geom[lev-1].periodicity());
186  //
187  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
188  // have been pre-filled - this includes ghost cells both inside and outside
189  // the domain
190  //
191  InterpFromCoarseLevel(base_state[lev], base_state[lev].nGrowVect(),
192  IntVect(0,0,0), // do not fill ghost cells outside the domain
193  base_state[lev-1], icomp, icomp, ncomp,
194  geom[lev-1], geom[lev],
195  refRatio(lev-1), &cell_cons_interp,
197 
198  // We need to do this here because the interpolation above may leave corners unfilled
199  // when the corners need to be filled by, for example, reflection of the fine ghost
200  // cell outside the fine region but inside the domain.
201  (*physbcs_base[lev])(base_state[lev],icomp,ncomp,base_state[lev].nGrowVect());
202 }
Here is the call graph for this function:

◆ interp_land_masks_from_coarse()

void ERF::interp_land_masks_from_coarse ( int  lev)
1979 {
1980  AMREX_ALWAYS_ASSERT(lev > 0);
1981 
1982  //
1983  // Only wrfinput fills these from a file. For every other init_type the values that
1984  // init_stuff left at this level are the ones that were asked for -- erf.is_land is
1985  // set per level -- and must not be replaced by the parent's.
1986  //
1987  // NOTE: init_from_metgrid also reads LANDMASK, but as a time series (one iMultiFab
1988  // per met_em time) rather than the single entry used here, so it needs its own
1989  // handling and is left alone.
1990  //
1991  if (solverChoice.init_type != InitType::WRFInput) { return; }
1992 
1993  auto entry = [] (Vector<Vector<std::unique_ptr<iMultiFab>>>& v, int l) -> iMultiFab*
1994  { return (v[l].empty()) ? nullptr : v[l][0].get(); };
1995 
1996  Vector<iMultiFab*> fine = {entry( lmask_lev,lev),
1997  entry(land_type_lev,lev),
1998  entry(soil_type_lev,lev)};
1999 
2000  Vector<iMultiFab*> crse = {entry( lmask_lev,lev-1),
2001  entry(land_type_lev,lev-1),
2002  entry(soil_type_lev,lev-1)};
2003 
2004  // Ratio of 1 in z: these live on ba2d, which has no vertical extent (see the note
2005  // in Interp2DArrays)
2006  const IntVect rr2d(refRatio(lev-1)[0], refRatio(lev-1)[1], 1);
2007 
2008  for (int n = 0; n < fine.size(); n++)
2009  {
2010  if (!fine[n] || !crse[n]) { continue; }
2011 
2012  // The land mask is the first of the three above
2013  const bool is_lmask = (n == 0);
2014 
2015  IntVect ngc = crse[n]->nGrowVect(); ngc[2] = 0;
2016  IntVect ngf = fine[n]->nGrowVect(); ngf[2] = 0;
2017 
2018  MultiFab crse_real(crse[n]->boxArray(), crse[n]->DistributionMap(), 1, ngc);
2019  MultiFab fine_real(fine[n]->boxArray(), fine[n]->DistributionMap(), 1, ngf);
2020 
2021  for (MFIter mfi(crse_real, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
2022  const Box& gbx = mfi.growntilebox(ngc);
2023  const Array4< Real>& dst_arr = crse_real.array(mfi);
2024  const Array4<const int >& src_arr = crse[n]->const_array(mfi);
2025  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
2026  {
2027  dst_arr(i,j,k) = static_cast<Real>(src_arr(i,j,k));
2028  });
2029  }
2030 
2031  //
2032  // DO fill the ghost cells outside the domain: SurfaceLayer::compute_fluxes reads
2033  // the land mask on grown boxes without clipping into the domain. The coarse
2034  // level has those cells filled -- init_from_wrfinput fills the grown box by
2035  // clamping into the valid region, init_stuff does a whole-array setVal, and this
2036  // routine fills them at a level with no file of its own.
2037  //
2038  InterpFromCoarseLevel(fine_real, ngf, ngf,
2039  crse_real, 0, 0, 1,
2040  geom[lev-1], geom[lev],
2041  rr2d, &pc_interp,
2043 
2044  for (MFIter mfi(fine_real, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
2045  const Box& gbx = mfi.growntilebox(ngf);
2046  const Array4< int>& dst_arr = fine[n]->array(mfi);
2047  const Array4<const Real>& src_arr = fine_real.const_array(mfi);
2048  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
2049  {
2050  const int injected = static_cast<int>(std::round(src_arr(i,j,k)));
2051  //
2052  // A land mask of 2 means a building, which init_zphys marks from this
2053  // level's own terrain blanking before we get here (see the ordering in
2054  // MakeNewLevelFromCoarse). That is finer information than anything the
2055  // parent can offer, so keep it. 2 is a legitimate category for the land
2056  // and soil types, hence the test on which array this is.
2057  //
2058  dst_arr(i,j,k) = (is_lmask && dst_arr(i,j,k) == 2) ? 2 : injected;
2059  });
2060  }
2061 
2062  // Prefer this level's own data in the ghost cells it shares with another fine box
2063  fine[n]->FillBoundary(geom[lev].periodicity());
2064  }
2065 }
Here is the call graph for this function:

◆ interp_mapfac_from_coarse()

void ERF::interp_mapfac_from_coarse ( int  lev)
1857 {
1858  AMREX_ALWAYS_ASSERT(lev > 0);
1859  AMREX_ALWAYS_ASSERT(mapfac[lev].size() == mapfac[lev-1].size());
1860 
1861  for (int i = 0; i < mapfac[lev].size(); i++)
1862  {
1863  //
1864  // With the isotropic MapFacType the _y entries alias the _x ones, so this loop
1865  // visits m_x, u_x and v_x exactly once; with the anisotropic version enabled it
1866  // visits all six.
1867  //
1868  if (!mapfac[lev][i] || !mapfac[lev-1][i]) { continue; }
1869 
1870  //
1871  // m_x is cell-centered, u_x lives on x-faces and v_x on y-faces, so each needs
1872  // the interpolater that matches its index type.
1873  //
1874  const IndexType ixtype = mapfac[lev][i]->boxArray().ixType();
1875  Interpolater* mapper = &cell_cons_interp;
1876  int bccomp = BCVars::cons_bc;
1877  if (ixtype.nodeCentered(0)) {
1878  mapper = &face_cons_linear_interp;
1879  bccomp = BCVars::xvel_bc;
1880  } else if (ixtype.nodeCentered(1)) {
1881  mapper = &face_cons_linear_interp;
1882  bccomp = BCVars::yvel_bc;
1883  }
1884 
1885  //
1886  // DO fill the ghost cells outside the domain: the coarse level's map factors are
1887  // defined there (init_from_wrfinput fills the grown box by clamping into the valid
1888  // region) and the advection routines read the map factors on grown boxes.
1889  //
1890  IntVect ngv = mapfac[lev][i]->nGrowVect(); ngv[2] = 0;
1891 
1892  // Ratio of 1 in z: the map factors live on ba2d, which has no vertical extent
1893  // (see the note in Interp2DArrays)
1894  const IntVect rr2d(refRatio(lev-1)[0], refRatio(lev-1)[1], 1);
1895 
1896  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
1897  // have been pre-filled - this includes ghost cells both inside and outside
1898  // the domain
1899  InterpFromCoarseLevel(*mapfac[lev][i], ngv, ngv,
1900  *mapfac[lev-1][i], 0, 0, 1,
1901  geom[lev-1], geom[lev],
1902  rr2d, mapper,
1903  domain_bcs_type, bccomp);
1904 
1905  // Prefer this level's own data in the ghost cells it shares with another fine box
1906  mapfac[lev][i]->FillBoundary(geom[lev].periodicity());
1907  }
1908 }
Here is the call graph for this function:

◆ interp_psfc_from_coarse()

void ERF::interp_psfc_from_coarse ( int  lev)
1922 {
1923  AMREX_ALWAYS_ASSERT(lev > 0);
1924 
1925  //
1926  // PSFC comes only from wrfinput. On any other pathway it is never filled at any
1927  // level, so there is nothing to interpolate and interpolating anyway would read
1928  // uninitialized data.
1929  //
1930  if (solverChoice.init_type != InitType::WRFInput) { return; }
1931 
1932  if (!mf_PSFC[lev] || !mf_PSFC[lev-1]) { return; }
1933 
1934  IntVect ngv = mf_PSFC[lev]->nGrowVect(); ngv[2] = 0;
1935 
1936  // Ratio of 1 in z: PSFC lives on ba2d, which has no vertical extent (see the note
1937  // in Interp2DArrays)
1938  const IntVect rr2d(refRatio(lev-1)[0], refRatio(lev-1)[1], 1);
1939 
1940  //
1941  // Do NOT fill the ghost cells outside the domain: unlike the fields that
1942  // init_from_wrfinput fills by clamping into the valid region, PSFC is copied out of
1943  // the file over the intersection with the domain, so the coarse level has nothing
1944  // there for us to interpolate. Nothing reads those cells -- update_sst_tsk clamps
1945  // its PSFC index into the domain.
1946  //
1947  InterpFromCoarseLevel(*mf_PSFC[lev], ngv, IntVect(0,0,0),
1948  *mf_PSFC[lev-1], 0, 0, 1,
1949  geom[lev-1], geom[lev],
1950  rr2d, &cell_cons_interp,
1952 
1953  // Prefer this level's own data in the ghost cells it shares with another fine box
1954  mf_PSFC[lev]->FillBoundary(geom[lev].periodicity());
1955 }
Here is the call graph for this function:

◆ is_it_time_for_action()

bool ERF::is_it_time_for_action ( int  nstep,
double  time,
double  dtlev,
int  action_interval,
double  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
664 {
665  bool int_test = (action_interval > 0 && nstep % action_interval == 0);
666 
667  bool per_test = false;
668  if (action_per > zero) {
669  const int num_per_old = static_cast<int>(amrex::Math::floor((time - dtlev) / action_per));
670  const int num_per_new = static_cast<int>(amrex::Math::floor((time) / action_per));
671 
672  if (num_per_old != num_per_new) {
673  per_test = true;
674  }
675  }
676 
677  return int_test || per_test;
678 }

◆ make_eb_box()

void ERF::make_eb_box ( )

◆ make_eb_regular()

void ERF::make_eb_regular ( )

◆ make_lsm_at_level()

void ERF::make_lsm_at_level ( int  lev,
bool from_regrid]  [[maybe_unused] = false 
)
private
1279 {
1280  int lsm_data_size = lsm.Get_Data_Size();
1281  int lsm_flux_size = lsm.Get_Flux_Size();
1282  lsm_data[lev].resize(lsm_data_size);
1283  lsm_data_name.resize(lsm_data_size);
1284  lsm_flux[lev].resize(lsm_flux_size);
1285  lsm_flux_name.resize(lsm_flux_size);
1286  lsm.Define(lev, solverChoice);
1287 
1288  // Check if we'll be using surface-only init (atmospheric state comes later from FillCoarsePatch)
1289  // Only applies to fine levels (lev > 0) with WRFInput during initial level creation.
1290  // Metgrid files only contain surface data anyway, so this doesn't apply to Metgrid.
1291  // During regrid (from_regrid=true), always initialize LSM immediately to avoid losing LSM state
1292  bool will_use_surface_only = false;
1293 #ifdef ERF_USE_NETCDF
1294  if (!from_regrid && lev > 0 && !nc_init_file[lev].empty() &&
1295  solverChoice.init_type == InitType::WRFInput) {
1296  will_use_surface_only = solverChoice.interp_atmos_from_coarse;
1297  }
1298 #endif
1299 
1300  // Only initialize LSM now if we're NOT using surface-only init
1301  // (for surface-only, LSM init must wait until after FillCoarsePatch provides atmospheric state)
1302  if (solverChoice.lsm_type != LandSurfaceType::None && !will_use_surface_only) {
1303  //
1304  // A level with no land file of its own takes its LSM state from level 0 rather
1305  // than from its parent (see NOAHMP::interp_from_lev0, which is handed Geom(0)
1306  // just below), so this must be the ratio between level 0 and this level -- the
1307  // product across every interface beneath it, not just the ratio across the last
1308  // one.
1309  //
1310  IntVect RefRatio(1);
1311  for (int l = 0; l < lev; ++l) { RefRatio *= refRatio(l); }
1312  lsm.Init(lev, vars_new[lev][Vars::cons], Geom(lev), Geom(0),
1313  domain_bcs_type, RefRatio, zero, nc_init_file); // dummy dt value
1314  }
1315 
1316  // Only access LSM data pointers if LSM has been initialized
1317  // (if using surface-only init, this will be done later after FillCoarsePatch)
1318  if (solverChoice.lsm_type != LandSurfaceType::None && !will_use_surface_only) {
1319  for (int mvar(0); mvar<lsm_data[lev].size(); ++mvar) {
1320  lsm_data[lev][mvar] = lsm.Get_Data_Ptr(lev,mvar);
1321  lsm_data_name[mvar] = lsm.Get_DataName(mvar);
1322  }
1323  for (int mvar(0); mvar<lsm_flux[lev].size(); ++mvar) {
1324  lsm_flux[lev][mvar] = lsm.Get_Flux_Ptr(lev,mvar);
1325  lsm_flux_name[mvar] = lsm.Get_FluxName(mvar);
1326  }
1327  if (lev>0) {
1328  lsm.Set_Lev0_Data_Ptr(lev);
1329  lsm.Set_Lev0_Flux_Ptr(lev);
1330  }
1331  }
1332 }
void Init(const int &lev, const amrex::MultiFab &cons_in, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt_advance, amrex::Vector< amrex::Vector< std::string >> &nc_init_file)
Definition: ERF_LandSurface.H:47
int Get_Data_Size()
Definition: ERF_LandSurface.H:128
void Set_Lev0_Data_Ptr(const int &lev)
Definition: ERF_LandSurface.H:155
std::string Get_DataName(const int &varIdx)
Definition: ERF_LandSurface.H:134
std::string Get_FluxName(const int &varIdx)
Definition: ERF_LandSurface.H:169
amrex::MultiFab * Get_Flux_Ptr(const int &lev, const int &varIdx)
Definition: ERF_LandSurface.H:122
void Set_Lev0_Flux_Ptr(const int &lev)
Definition: ERF_LandSurface.H:175
void Define(const int &lev, SolverChoice &sc)
Definition: ERF_LandSurface.H:40
int Get_Flux_Size()
Definition: ERF_LandSurface.H:131
bool interp_atmos_from_coarse
Definition: ERF_DataStruct.H:2293

◆ make_physbcs()

void ERF::make_physbcs ( int  lev)
private
1306 {
1307  if (SolverChoice::mesh_type == MeshType::VariableDz) {
1308  AMREX_ALWAYS_ASSERT(z_phys_nd[lev] != nullptr);
1309  }
1310 
1311  // Real (wrfbdy/metgrid) boundary data only exists at level 0 -- there is no machinery
1312  // to interpolate it onto a refined level. At lev > 0 we must therefore fall back on
1313  // the boundary condition types specified in the inputs file, so that the lateral
1314  // ghost cells outside the domain get filled if a fine grid abuts a domain boundary.
1315  bool l_use_real_bcs = (solverChoice.use_real_bcs && (lev == 0));
1316 
1317  physbcs_cons[lev] = std::make_unique<ERFPhysBCFunct_cons> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
1319  z_phys_nd[lev], l_use_real_bcs, th_bc_data[lev].data());
1320  physbcs_u[lev] = std::make_unique<ERFPhysBCFunct_u> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
1322  z_phys_nd[lev], l_use_real_bcs, xvel_bc_data[lev].data());
1323  physbcs_v[lev] = std::make_unique<ERFPhysBCFunct_v> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
1325  z_phys_nd[lev], l_use_real_bcs, yvel_bc_data[lev].data());
1326  physbcs_w[lev] = std::make_unique<ERFPhysBCFunct_w> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d,
1329  l_use_real_bcs, zvel_bc_data[lev].data());
1330  physbcs_base[lev] = std::make_unique<ERFPhysBCFunct_base> (lev, geom[lev], domain_bcs_type, domain_bcs_type_d, z_phys_nd[lev],
1331  (solverChoice.terrain_type == TerrainType::MovingFittedMesh),
1332  (solverChoice.mesh_type != MeshType::ConstantDz),
1334 }
Here is the call graph for this function:

◆ make_subdomains()

void ERF::make_subdomains ( const amrex::BoxList &  ba,
amrex::Vector< amrex::BoxArray > &  bins 
)

Group a list of boxes into subdomains based on adjacency.

Parameters
[in]blList of boxes to partition.
[out]binsVector of BoxArrays containing the grouped subdomains.
12 {
13  Vector<BoxList> bins_bl;
14 
15  // Clear out any old bins
16  bins.clear();
17 
18  // Iterate over boxes
19  for (auto bx : bl)
20  {
21  Box gbx(bx); gbx.grow(1);
22 
23  // Find *every* existing bin this box touches. Stopping at the first match
24  // is not enough: a box that bridges two bins connects them, so those bins
25  // have to be merged. Otherwise the two halves remain separate subdomains
26  // even though they are now adjacent through bx, which breaks the invariant
27  // that no box in a subdomain touches a box in any other subdomain.
28  Vector<int> touched;
29  for (int j = 0; j < bins_bl.size(); ++j) {
30  for (auto const& b : bins_bl[j])
31  {
32  if (gbx.intersects(b)) {
33  touched.push_back(j);
34  break;
35  }
36  }
37  }
38 
39  if (touched.empty()) {
40  // Box touches nothing seen so far, so it starts a new bin
41  BoxList new_bin;
42  new_bin.push_back(bx);
43  bins_bl.push_back(new_bin);
44  } else {
45  // Keep the first bin touched and fold the others, plus bx, into it.
46  // Erasing from the back leaves the lower indices -- including the one
47  // we hold a reference to -- valid.
48  BoxList& keep = bins_bl[touched[0]];
49  keep.push_back(bx);
50  for (int n = int(touched.size())-1; n >= 1; --n) {
51  keep.join(bins_bl[touched[n]]);
52  bins_bl.erase(bins_bl.begin() + touched[n]);
53  }
54  }
55  }
56 
57 #ifdef AMREX_DEBUG
58  // Verify the invariant we just worked to maintain: boxes in different bins
59  // must not touch, else the Poisson solve would treat one connected region as
60  // two independent problems with bogus conditions at their shared interface.
61  for (int i = 0; i < bins_bl.size(); ++i) {
62  for (int j = i+1; j < bins_bl.size(); ++j) {
63  for (auto const& bi : bins_bl[i]) {
64  Box gbi(bi); gbi.grow(1);
65  for (auto const& bj : bins_bl[j]) {
66  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!gbi.intersects(bj),
67  "make_subdomains: boxes in different subdomains are adjacent");
68  }
69  }
70  }
71  }
72 #endif
73 
74  // Convert the BoxLists to BoxArrays
75  for (int i = 0; i < bins_bl.size(); ++i) {
76  bins.push_back(BoxArray(bins_bl[i]));
77  }
78 }
real(c_double), private bi
Definition: ERF_module_mp_morr_two_moment.F90:182
Here is the call graph for this function:

◆ MakeDiagnosticAverage()

void ERF::MakeDiagnosticAverage ( amrex::Vector< amrex::Real > &  h_havg,
amrex::MultiFab &  S,
int  n 
)
3499 {
3500  // Get the number of cells in z at level 0
3501  int dir_z = AMREX_SPACEDIM-1;
3502  auto domain = geom[0].Domain();
3503  int size_z = domain.length(dir_z);
3504  int start_z = domain.smallEnd()[dir_z];
3505  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
3506 
3507  // resize the level 0 horizontal average vectors
3508  h_havg.resize(size_z, 0.0_rt);
3509 
3510  // Get the cell centered data and construct sums
3511  //
3512  // NOTE: deliberately not threaded. Every iteration does "h_havg[k] += ..." into the
3513  // same shared Vector with no atomic and no per-thread partials, so threading it
3514  // would be an outright data race on top of an order-dependent sum. The body is
3515  // a pure reduction, so running it serially costs little.
3516  //
3517  for (MFIter mfi(S); mfi.isValid(); ++mfi) {
3518  const Box& box = mfi.validbox();
3519  const IntVect& se = box.smallEnd();
3520  const IntVect& be = box.bigEnd();
3521 
3522  auto fab_arr = S[mfi].array();
3523 
3524  FArrayBox fab_reduce(box, 1, The_Async_Arena());
3525  auto arr_reduce = fab_reduce.array();
3526 
3527  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
3528  arr_reduce(i, j, k, 0) = fab_arr(i,j,k,n);
3529  });
3530 
3531  for (int k=se[dir_z]; k <= be[dir_z]; ++k) {
3532  Box kbox(box); kbox.setSmall(dir_z,k); kbox.setBig(dir_z,k);
3533  h_havg[k-start_z] += fab_reduce.sum<RunOn::Device>(kbox,0);
3534  }
3535  }
3536 
3537  // combine sums from different MPI ranks
3538  ParallelDescriptor::ReduceRealSum(h_havg.dataPtr(), static_cast<int>(h_havg.size()));
3539 
3540  // divide by the total number of cells we are averaging over
3541  for (int k = 0; k < size_z; ++k) {
3542  h_havg[k] /= area_z;
3543  }
3544 }
Here is the call graph for this function:

◆ MakeEBGeometry()

void ERF::MakeEBGeometry ( )

◆ MakeFilename_EyeTracker_latlon()

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

◆ MakeFilename_EyeTracker_maxvel()

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

◆ MakeFilename_EyeTracker_minpressure()

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

◆ MakeHorizontalAverages()

void ERF::MakeHorizontalAverages ( )
3393 {
3394  int lev = 0;
3395 
3396  // First, average down all levels (if doing two-way coupling)
3397  if (solverChoice.coupling_type == CouplingType::TwoWay) {
3398  AverageDown();
3399  }
3400 
3401  MultiFab mf(grids[lev], dmap[lev], 5, 0);
3402 
3403  int zdir = 2;
3404  auto domain = geom[0].Domain();
3405 
3406  bool use_moisture = (solverChoice.moisture_type != MoistureType::None);
3407  bool is_anelastic = (solverChoice.anelastic[lev] == 1);
3408 
3409  for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
3410  const Box& bx = mfi.validbox();
3411  auto fab_arr = mf.array(mfi);
3412  auto const hse_arr = base_state[lev].const_array(mfi);
3413  auto const cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
3414  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
3415  Real dens = cons_arr(i, j, k, Rho_comp);
3416  fab_arr(i, j, k, 0) = dens;
3417  fab_arr(i, j, k, 1) = cons_arr(i, j, k, RhoTheta_comp) / dens;
3418  if (!use_moisture) {
3419  if (is_anelastic) {
3420  fab_arr(i,j,k,2) = hse_arr(i,j,k,BaseState::p0_comp);
3421  } else {
3422  fab_arr(i,j,k,2) = getPgivenRTh(cons_arr(i,j,k,RhoTheta_comp));
3423  }
3424  }
3425  });
3426  }
3427 
3428  if (use_moisture)
3429  {
3430  for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
3431  const Box& bx = mfi.validbox();
3432  auto fab_arr = mf.array(mfi);
3433  auto const hse_arr = base_state[lev].const_array(mfi);
3434  auto const cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
3435  int ncomp = vars_new[lev][Vars::cons].nComp();
3436 
3437  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
3438  Real dens = cons_arr(i, j, k, Rho_comp);
3439  if (is_anelastic) {
3440  fab_arr(i,j,k,2) = hse_arr(i,j,k,BaseState::p0_comp);
3441  } else {
3442  Real qv = cons_arr(i, j, k, RhoQ1_comp) / dens;
3443  fab_arr(i, j, k, 2) = getPgivenRTh(cons_arr(i, j, k, RhoTheta_comp), qv);
3444  }
3445  fab_arr(i, j, k, 3) = (ncomp > RhoQ1_comp ? cons_arr(i, j, k, RhoQ1_comp) / dens : zero);
3446  fab_arr(i, j, k, 4) = (ncomp > RhoQ2_comp ? cons_arr(i, j, k, RhoQ2_comp) / dens : zero);
3447  });
3448  }
3449 
3450  Gpu::HostVector<Real> h_avg_qv = sumToLine(mf,3,1,domain,zdir);
3451  Gpu::HostVector<Real> h_avg_qc = sumToLine(mf,4,1,domain,zdir);
3452  }
3453 
3454  // Sum in the horizontal plane
3455  Gpu::HostVector<Real> h_avg_density = sumToLine(mf,0,1,domain,zdir);
3456  Gpu::HostVector<Real> h_avg_temperature = sumToLine(mf,1,1,domain,zdir);
3457  Gpu::HostVector<Real> h_avg_pressure = sumToLine(mf,2,1,domain,zdir);
3458 
3459  // Divide by the total number of cells we are averaging over
3460  int size_z = domain.length(zdir);
3461  Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
3462  int klen = static_cast<int>(h_avg_density.size());
3463 
3464  for (int k = 0; k < klen; ++k) {
3465  h_havg_density[k] /= area_z;
3466  h_havg_temperature[k] /= area_z;
3467  h_havg_pressure[k] /= area_z;
3468  if (solverChoice.moisture_type != MoistureType::None)
3469  {
3470  h_havg_qc[k] /= area_z;
3471  h_havg_qv[k] /= area_z;
3472  }
3473  } // k
3474 
3475  // resize device vectors
3476  d_havg_density.resize(size_z, 0.0_rt);
3477  d_havg_temperature.resize(size_z, 0.0_rt);
3478  d_havg_pressure.resize(size_z, 0.0_rt);
3479 
3480  // copy host vectors to device vectors
3481  Gpu::copy(Gpu::hostToDevice, h_havg_density.begin(), h_havg_density.end(), d_havg_density.begin());
3482  Gpu::copy(Gpu::hostToDevice, h_havg_temperature.begin(), h_havg_temperature.end(), d_havg_temperature.begin());
3483  Gpu::copy(Gpu::hostToDevice, h_havg_pressure.begin(), h_havg_pressure.end(), d_havg_pressure.begin());
3484 
3485  if (solverChoice.moisture_type != MoistureType::None)
3486  {
3487  d_havg_qv.resize(size_z, 0.0_rt);
3488  d_havg_qc.resize(size_z, 0.0_rt);
3489  Gpu::copy(Gpu::hostToDevice, h_havg_qv.begin(), h_havg_qv.end(), d_havg_qv.begin());
3490  Gpu::copy(Gpu::hostToDevice, h_havg_qc.begin(), h_havg_qc.end(), d_havg_qc.begin());
3491  }
3492 }
amrex::Gpu::DeviceVector< amrex::Real > d_havg_temperature
Definition: ERF.H:1837
amrex::Gpu::DeviceVector< amrex::Real > d_havg_qv
Definition: ERF.H:1839
amrex::Vector< amrex::Real > h_havg_pressure
Definition: ERF.H:1832
amrex::Vector< amrex::Real > h_havg_qc
Definition: ERF.H:1834
amrex::Vector< amrex::Real > h_havg_density
Definition: ERF.H:1830
amrex::Gpu::DeviceVector< amrex::Real > d_havg_qc
Definition: ERF.H:1840
amrex::Gpu::DeviceVector< amrex::Real > d_havg_density
Definition: ERF.H:1836
amrex::Vector< amrex::Real > h_havg_temperature
Definition: ERF.H:1831
amrex::Gpu::DeviceVector< amrex::Real > d_havg_pressure
Definition: ERF.H:1838
amrex::Vector< amrex::Real > h_havg_qv
Definition: ERF.H:1833
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
325 {
326  //
327  // Note that "time" here is elapsed time
328  //
329  AMREX_ALWAYS_ASSERT(lev > 0);
330 
331  if (verbose) {
332  amrex::Print() <<" NEW BA FROM COARSE AT LEVEL " << lev << " " << ba << std::endl;
333  }
334 
335  //
336  // Grow the subdomains vector and build the subdomains vector at this level
337  //
338  subdomains.resize(lev+1);
339  //
340  // Create subdomains at each level within the domain such that
341  // 1) all boxes in a given subdomain are "connected"
342  // 2) no boxes in a subdomain touch any boxes in any other subdomain
343  //
344  make_subdomains(ba.simplified_list(), subdomains[lev]);
345 
346  // ****************************************************************************************
347  // Define grids[lev]/dmap[lev] to be the passed-in ba/dm *before* usage.
348  //
349  // AmrCore::regrid does not call SetBoxArray/SetDistributionMap for this new level
350  // until MakeNewLevelFromCoarse returns, so grids[lev]/dmap[lev] are still empty here.
351  // Any subsequent calls that utilize grids[lev]/dmap[lev] will then fail (e.g. anelastic
352  // velocity projection below).
353  // ****************************************************************************************
354  SetBoxArray(lev, ba);
355  SetDistributionMap(lev, dm);
356 
357  if (lev == 0) init_bcs();
358 
359  //********************************************************************************************
360  // This allocates all kinds of things, including but not limited to: solution arrays,
361  // terrain arrays, ba2d, metric terms and base state.
362  // *******************************************************************************************
363  init_stuff(lev, ba, dm, vars_new[lev], vars_old[lev], base_state[lev], z_phys_nd[lev]);
364 
365  //
366  // Note that t_new = time here is elapsed time
367  //
368  t_new[lev] = time;
369  t_old[lev] = time - bogus_large_value;
370 
371  // ********************************************************************************************
372  // Build the data structures for metric quantities used with terrain-fitted coordinates
373  // ********************************************************************************************
374  if ( solverChoice.terrain_type == TerrainType::EB ||
375  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
376  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
377  {
378  const amrex::EB2::IndexSpace& ebis = amrex::EB2::IndexSpace::top();
379  const EB2::Level& eb_level = ebis.getLevel(geom[lev]);
380  if (solverChoice.terrain_type == TerrainType::EB) {
381  eb[lev]->make_all_factories(lev, geom[lev], ba, dm, eb_level);
382  } else if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
383  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
384 #if USE_FC_FACTORY
385  eb[lev]->make_all_factories(lev, geom[lev], ba, dm, eb_level);
386 #else
387  eb[lev]->make_cc_factory(lev, geom[lev], ba, dm, eb_level);
388 #endif
389  }
390  }
391  init_zphys(lev, time);
393 
394  //
395  // Make sure that detJ is the average of the data on a finer level if there is one
396  // *and* if there is two-way coupling
397  //
398  if ( (SolverChoice::mesh_type != MeshType::ConstantDz) && (solverChoice.coupling_type == CouplingType::TwoWay) ) {
399  // NOTE: z_phys_cc is deliberately NOT averaged down. Every level's base state is
400  // built to be in discrete hydrostatic balance against that level's own
401  // cell-centered heights, so replacing the coarse heights with the average of
402  // the fine ones -- after the base states have already been built -- leaves the
403  // coarse base state out of balance with the heights the dycore then uses for
404  // vertical gradients, Rayleigh damping and the sponge zones. z_phys_cc is also
405  // derived from z_phys_nd, which is not averaged down either, so averaging only
406  // the cell-centered heights made the two disagree inside the refined region.
407  //
408  // detJ IS still averaged down: AverageDownTo weights (rho S) by detJ_cc before
409  // averaging and divides by it afterwards, so the coarse detJ must be the average
410  // of the fine detJ for that average-down to telescope and stay conservative.
411  for (int crse_lev = lev-1; crse_lev >= 0; crse_lev--) {
412  average_down( *detJ_cc[crse_lev+1], *detJ_cc[crse_lev], 0, 1, refRatio(crse_lev));
413  }
414  }
415 
416  // ********************************************************************************************
417  // Build the data structures for canopy model (depends upon z_phys)
418  // ********************************************************************************************
420  m_forest_drag[lev]->define_drag_field(ba, dm, geom[lev],
421  z_phys_cc[lev].get(), z_phys_nd[lev].get(),
424  }
425 
426  //********************************************************************************************
427  // Radiation
428  // *******************************************************************************************
430  {
431  rad[lev]->Init(geom[lev], ba, &vars_new[lev][Vars::cons]);
432  }
433 
434  // *****************************************************************************************************
435  // Initialize the boundary conditions (after initializing the terrain but before calling
436  // initHSE or FillCoarsePatch)
437  // *****************************************************************************************************
438  make_physbcs(lev);
439 
440  // ********************************************************************************************
441  // Update the base state at this level by interpolation from coarser level
442  // ********************************************************************************************
443  InterpFromCoarseLevel(base_state[lev], base_state[lev].nGrowVect(),
444  IntVect(0,0,0), // do not fill ghost cells outside the domain
445  base_state[lev-1], 0, 0, base_state[lev].nComp(),
446  geom[lev-1], geom[lev],
447  refRatio(lev-1), &cell_cons_interp,
449 
450  // Impose bc's outside the domain
451  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
452 
453  //********************************************************************************************
454  // Microphysics
455  // *******************************************************************************************
456  int q_size = micro->Get_Qmoist_Size(lev);
457  qmoist[lev].resize(q_size);
458  micro->Define(lev, solverChoice);
459  if (solverChoice.moisture_type != MoistureType::None)
460  {
461  micro->Init(lev, vars_new[lev][Vars::cons],
462  grids[lev], Geom(lev), zero,
463  z_phys_nd[lev], detJ_cc[lev]); // dummy dt value
464  // Refresh the land/water mask pointer. Must be re-issued at every
465  // micro->Init site: init_stuff rebuilds lmask_lev[lev][0], so a stale
466  // pointer would dangle. No-op for every scheme except WDM6.
467  micro->Set_Lmask(lev, (lmask_lev[lev].empty()) ? nullptr : lmask_lev[lev][0].get());
468  }
469  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
470  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
471  }
472 
473  // ********************************************************************************************
474  // Build the data structures for calculating diffusive/turbulent terms
475  // ********************************************************************************************
476  update_diffusive_arrays(lev, ba, dm);
477 
478  // ********************************************************************************************
479  // Build the data structures for holding sea surface temps and skin temps
480  // ********************************************************************************************
481  sst_lev[lev].resize(1); sst_lev[lev][0] = nullptr;
482  tsk_lev[lev].resize(1); tsk_lev[lev][0] = nullptr;
483 
484  // ********************************************************************************************
485  // Fill data at the new level by interpolation from the coarser level
486  // Note that internal to FillCoarsePatch we will convert velocity to momentum,
487  // then interpolate momentum, then convert momentum back to velocity
488  // Also note that FillCoarsePatch is hard-wired to act only on lev_new at coarse and fine
489  // ********************************************************************************************
490 
491 #ifdef ERF_USE_NETCDF
492  if ( ( (solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid) ) &&
493  !nc_init_file[lev].empty() )
494  {
495  // Just making sure that ghost cells aren't uninitialized...
496  vars_new[lev][Vars::cons].setVal(0.0); vars_old[lev][Vars::cons].setVal(0.0);
497  vars_new[lev][Vars::xvel].setVal(0.0); vars_old[lev][Vars::xvel].setVal(0.0);
498  vars_new[lev][Vars::yvel].setVal(0.0); vars_old[lev][Vars::yvel].setVal(0.0);
499  vars_new[lev][Vars::zvel].setVal(0.0); vars_old[lev][Vars::zvel].setVal(0.0);
500 
501  AMREX_ALWAYS_ASSERT(solverChoice.terrain_type == TerrainType::StaticFittedMesh);
502 
503  //
504  // CHOOSE INITIALIZATION PATH:
505  // If interp_atmos_from_coarse is enabled for a finer level with WRFInput,
506  // read only surface fields from wrfinput and interpolate atmospheric state from coarse.
507  // Metgrid files only contain surface data anyway, so use_surface_only doesn't apply.
508  //
509  bool use_surface_only = solverChoice.interp_atmos_from_coarse && (lev > 0) &&
510  (solverChoice.init_type == InitType::WRFInput);
511 
512  // Tell advance_radiation whether this level's atmospheric state is about to come from
513  // FillCoarsePatch, in which case it must interpolate the radiation fields from the
514  // parent for one step rather than run RRTMGP on a state that is not yet consistent.
515  if (static_cast<int>(rad_interp_from_coarse_pending.size()) <= lev) {
516  rad_interp_from_coarse_pending.resize(lev+1, 0);
517  }
518  rad_interp_from_coarse_pending[lev] = use_surface_only ? 1 : 0;
519 
520  // If using surface-only init with LSM, we need to resize the lsm_data/lsm_flux vectors
521  // before the deferred lsm.Init() call below. The LSM will read its initial state
522  // from nc_init_file when lsm.Init() is called (after FillCoarsePatch provides
523  // atmospheric data). Note: Define() is empty for both LSMs.
524  if (use_surface_only && solverChoice.lsm_type != LandSurfaceType::None) {
525  int lsm_data_size = lsm.Get_Data_Size();
526  int lsm_flux_size = lsm.Get_Flux_Size();
527  lsm_data[lev].resize(lsm_data_size);
528  lsm_data_name.resize(lsm_data_size);
529  lsm_flux[lev].resize(lsm_flux_size);
530  lsm_flux_name.resize(lsm_flux_size);
531  lsm.Define(lev, solverChoice);
532  }
533 
534  if (solverChoice.init_type == InitType::Metgrid) {
535  init_from_metgrid(lev);
536  } else if (solverChoice.init_type == InitType::WRFInput) {
537  if (use_surface_only) {
538  amrex::Print() << "Using interp_atmos_from_coarse mode at level " << lev << ":\n";
539  amrex::Print() << " - Reading surface fields from wrfinput\n";
540  amrex::Print() << " - Atmospheric state will be interpolated from level " << lev-1 << "\n";
541  init_from_wrfinput(lev, *mf_PSFC[lev], /*read_atmos_state*/ false);
542  } else {
543  init_from_wrfinput(lev, *mf_PSFC[lev]);
544  }
545  }
546  init_zphys(lev, time);
548  make_physbcs(lev);
549 
550  dz_min[lev] = (*detJ_cc[lev]).min(0) * geom[lev].CellSize(2);
551 
552  //
553  // If we used surface-only init, we need to rebuild the base state and
554  // interpolate the atmospheric state from coarse (just like a level with no init file)
555  //
556  if (use_surface_only) {
557  // Rebuild base state from wrfinput on top of the fine terrain (must happen before
558  // FillCoarsePatch, which interpolates perturbational quantities relative to base state)
559  rebuild_base_state_from_wrfinput(lev, base_state[lev]);
560  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
561 
562  // Interpolate atmospheric state from coarse level.
563  // NOTE: This creates thermodynamically inconsistent data because ρ, θ, qv are
564  // interpolated independently. We rely on:
565  // 1. Skipping radiation on the first timestep (see ERF_AdvanceRadiation.cpp)
566  // 2. Letting dynamics equilibrate the state on the first timestep
567  FillCoarsePatch(lev, time);
568 
569  // Now initialize LSM with the properly filled atmospheric state.
570  // The LSM will read its soil/vegetation initial state from nc_init_file
571  // (e.g., Noah-MP reads via the noahmpio library in Fortran).
572  if (solverChoice.lsm_type != LandSurfaceType::None) {
573  amrex::Print() << "Initializing LSM at level " << lev << " after FillCoarsePatch\n";
574 
575  IntVect RefRatio(1);
576  for (int l = 0; l < lev; ++l) { RefRatio *= refRatio(l); }
577  lsm.Init(lev, vars_new[lev][Vars::cons], Geom(lev), Geom(0),
578  domain_bcs_type, RefRatio, zero, nc_init_file);
579 
580  // Set up the LSM data/flux pointers (same as in make_lsm_at_level)
581  for (int mvar(0); mvar<lsm_data[lev].size(); ++mvar) {
582  lsm_data[lev][mvar] = lsm.Get_Data_Ptr(lev,mvar);
583  lsm_data_name[mvar] = lsm.Get_DataName(mvar);
584  }
585  for (int mvar(0); mvar<lsm_flux[lev].size(); ++mvar) {
586  lsm_flux[lev][mvar] = lsm.Get_Flux_Ptr(lev,mvar);
587  lsm_flux_name[mvar] = lsm.Get_FluxName(mvar);
588  }
589  if (lev>0) {
590  lsm.Set_Lev0_Data_Ptr(lev);
591  lsm.Set_Lev0_Flux_Ptr(lev);
592  }
593  }
594  }
595 
596  } else {
597 #endif
598 
599 #ifdef ERF_USE_NETCDF
600  //
601  // A WRFInput level created by refinement rather than read from a file: build its base
602  // state by the same construction level 0 used, on top of the coarse interpolation done
603  // above. This must happen BEFORE FillCoarsePatch, which interpolates perturbational
604  // quantities relative to the base state.
605  //
606  if ( (solverChoice.init_type == InitType::WRFInput) && nc_init_file[lev].empty() ) {
607  rebuild_base_state_from_wrfinput(lev, base_state[lev]);
608  (*physbcs_base[lev])(base_state[lev],0,base_state[lev].nComp(),base_state[lev].nGrowVect());
609  }
610 #endif
611 
612  //
613  // Interpolate the solution data
614  //
615  FillCoarsePatch(lev, time);
616 
617  //
618  // Interpolate the 2D arrays at the lower boundary
619  // Note that ba2d is constructed already in init_stuff, but we have not yet defined dmap[lev]
620  // so we must explicitly pass dm.
621  Interp2DArrays(lev,ba2d[lev],dm);
622 
623  // Populate dz_min for dynamically-created fine levels (non-terrain path).
624  if (static_cast<int>(dz_min.size()) <= lev) { dz_min.resize(lev+1); }
625  dz_min[lev] = geom[lev].CellSize(2);
626  if ( SolverChoice::mesh_type != MeshType::ConstantDz && detJ_cc[lev] ) {
627  dz_min[lev] *= (*detJ_cc[lev]).min(0);
628  }
629 #ifdef ERF_USE_NETCDF
630  }
631 #endif
632 
633  // ********************************************************************************************
634  // Initialize the integrator class
635  // ********************************************************************************************
636  dt_mri_ratio[lev] = dt_mri_ratio[lev-1];
638 
639  // ********************************************************************************************
640  // If we are making a new level then the FillPatcher for this level hasn't been allocated yet
641  // ********************************************************************************************
642  if (lev > 0 && cf_width >= 0) {
645  }
646 
647  // ********************************************************************************************
648  // For anelastic levels created from coarse (either on restart or during a run), project the
649  // interpolated velocity to enforce the divergence-free constraint. This Initializes gradp[lev]
650  // via the pressure projection, handling both the pure-anelastic case and the hybrid case
651  // (compressible lev-1, anelastic lev) where there is no coarse gradp to interpolate.
652  // FillPatchers must be constructed above before this call. pp_inc is scratch; zero afterward.
653  // ********************************************************************************************
654  if (solverChoice.anelastic[lev]) {
655  double dummy_dt = 1.0;
656  project_initial_velocity(lev, time, dummy_dt);
657  pp_inc[lev].setVal(0.0);
658  }
659 
660  //********************************************************************************************
661  // Land Surface Model - setup data structures
662  // NOTE: Actual LSM initialization (lsm.Init) is deferred if using interp_atmos_from_coarse
663  // because it needs valid atmospheric state from FillCoarsePatch
664  // *******************************************************************************************
665  make_lsm_at_level(lev);
666 
667  // ********************************************************************************************
668  // Create the SurfaceLayer arrays at this (new) level
669  // ********************************************************************************************
670  for (OrientationIter oit; oit; ++oit) {
671  Orientation ori = oit();
672  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
673  Vector<MultiFab*> mfv_old = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::xvel],
674  &vars_old[lev][Vars::yvel], &vars_old[lev][Vars::zvel]};
675  m_SurfaceLayer[ori]->make_SurfaceLayer_at_level(lev,lev+1,
676  mfv_old, Theta_prim[lev], Qv_prim[lev],
677  Qr_prim[lev], z_phys_nd[lev],
678  Hwave[lev].get(), Lwave[lev].get(), eddyDiffs_lev[lev].get(),
680  sst_lev[lev], tsk_lev[lev], lmask_lev[lev]);
681  }
682  }
683 
684  // ********************************************************************************************
685  // Set up the Rayleigh damping vectors at this (new) level
686  // ********************************************************************************************
689  {
691  }
692 
693 }
void update_diffusive_arrays(int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
Definition: ERF_MakeNewArrays.cpp:684
void initialize_integrator(int lev, amrex::MultiFab &cons_mf, amrex::MultiFab &vel_mf)
Definition: ERF_MakeNewArrays.cpp:1282
void make_subdomains(const amrex::BoxList &ba, amrex::Vector< amrex::BoxArray > &bins)
Group a list of boxes into subdomains based on adjacency.
Definition: ERF_MakeSubdomains.cpp:11
void update_terrain_arrays(int lev)
Definition: ERF_MakeNewArrays.cpp:1265
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:25
void Define_ERFFillPatchers(int lev)
Definition: ERF.cpp:3573
void make_lsm_at_level(int lev, bool from_regrid[[maybe_unused]]=false)
Definition: ERF_MakeNewLevel.cpp:1278
void init_zphys(int lev, double elapsed_time)
Definition: ERF_MakeNewArrays.cpp:884
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
28 {
29  //
30  // Note that "time" here is elapsed time
31  //
32 
33  // A level built here gets its atmospheric state from its own initialization -- an input
34  // file, a problem setup or a restart -- rather than from FillCoarsePatch, so radiation
35  // has a consistent state to work with and nothing is pending for it. Clearing this
36  // explicitly matters because the entry may be left over from an earlier life of this
37  // level: erf.interp_atmos_from_coarse is ignored on this path (see the warning below),
38  // so a stale flag would skip a step of radiation for no reason.
39  if (static_cast<int>(rad_interp_from_coarse_pending.size()) <= lev) {
40  rad_interp_from_coarse_pending.resize(lev+1, 0);
41  }
43 
44  BoxArray ba;
45  DistributionMapping dm;
46  Box domain(Geom(0).Domain());
47  if (lev == 0 && restart_chkfile.empty() &&
48  (max_grid_size[0][0] >= domain.length(0)) &&
49  (max_grid_size[0][1] >= domain.length(1)) &&
50  ba_in.size() != ParallelDescriptor::NProcs())
51  {
52  // We only decompose in z if max_grid_size_z indicates we should,
53  // and if we are allowed to split boxes in z at all
54  // (amr.no_box_split_dir = 2, the ERF default, forbids that)
55  bool decompose_in_z = (max_grid_size[0][2] < domain.length(2)) &&
56  (no_box_split_dir != 2);
57 
58  ba = ERFPostProcessBaseGrids(Geom(0).Domain(),decompose_in_z);
59  dm = DistributionMapping(ba);
60  } else {
61  ba = ba_in;
62  dm = dm_in;
63  }
64 
65  // ********************************************************************************************
66  // Define grids[lev] to be ba
67  // ********************************************************************************************
68  SetBoxArray(lev, ba);
69 
70  // ********************************************************************************************
71  // Define dmap[lev] to be dm
72  // ********************************************************************************************
73  SetDistributionMap(lev, dm);
74 
75  if (verbose) {
76  amrex::Print() << "BA FROM SCRATCH AT LEVEL " << lev << " " << ba << std::endl;
77  // amrex::Print() <<" SIMPLIFIED BA FROM SCRATCH AT LEVEL " << lev << " " << ba.simplified_list() << std::endl;
78  }
79 
80  subdomains.resize(lev+1);
81  //
82  // Create subdomains at each level within the domain such that
83  // 1) all boxes in a given subdomain are "connected"
84  // 2) no boxes in a subdomain touch any boxes in any other subdomain
85  //
86  make_subdomains(ba.simplified_list(), subdomains[lev]);
87 
88  if (lev == 0) init_bcs();
89 
90  if ( solverChoice.terrain_type == TerrainType::EB ||
91  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
92  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
93  {
94  const amrex::EB2::IndexSpace& ebis = amrex::EB2::IndexSpace::top();
95  const EB2::Level& eb_level = ebis.getLevel(geom[lev]);
96  if (solverChoice.terrain_type == TerrainType::EB) {
97  eb[lev]->make_all_factories(lev, geom[lev], grids[lev], dmap[lev], eb_level);
98  } else if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
99  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
100 #if USE_FC_FACTORY
101  eb[lev]->make_all_factories(lev, geom[lev], grids[lev], dmap[lev], eb_level);
102 #else
103  eb[lev]->make_cc_factory(lev, geom[lev], grids[lev], dmap[lev], eb_level);
104 #endif
105  }
106  }
107 
108  auto& lev_new = vars_new[lev];
109  auto& lev_old = vars_old[lev];
110 
111  //********************************************************************************************
112  // This allocates all kinds of things, including but not limited to: solution arrays,
113  // terrain arrays, metric terms and base state.
114  // *******************************************************************************************
115  init_stuff(lev, ba, dm, lev_new, lev_old, base_state[lev], z_phys_nd[lev]);
116 
117  //********************************************************************************************
118  // Land Surface Model
119  // *******************************************************************************************
120  // In MakeNewLevelFromScratch, both levels are created at startup and read from their
121  // respective wrfinput files, so there's no surface-only initialization. Pass from_regrid=true
122  // to prevent deferring LSM initialization (deferred init only applies in MakeNewLevelFromCoarse
123  // when interp_atmos_from_coarse is enabled).
124  make_lsm_at_level(lev, true);
125 
126  // ********************************************************************************************
127  // Build the data structures for calculating diffusive/turbulent terms
128  // ********************************************************************************************
129  update_diffusive_arrays(lev, ba, dm);
130 
131  // ********************************************************************************************
132  // Build the data structures for holding sea surface temps and skin temps
133  // ********************************************************************************************
134  sst_lev[lev].resize(1); sst_lev[lev][0] = nullptr;
135  tsk_lev[lev].resize(1); tsk_lev[lev][0] = nullptr;
136 
137  // ********************************************************************************************
138  // Thin immersed body
139  // *******************************************************************************************
140  init_thin_body(lev, ba, dm);
141 
142  // ********************************************************************************************
143  // Initialize the integrator class
144  // ********************************************************************************************
145  initialize_integrator(lev, lev_new[Vars::cons],lev_new[Vars::xvel]);
146 
147  // ********************************************************************************************
148  // Initialize the data itself
149  // If (init_type == InitType::WRFInput) then we are initializing terrain and the initial data in
150  // the same call so we must call init_only before update_terrain_arrays
151  // If (init_type != InitType::WRFInput) then we want to initialize the terrain before the initial data
152  // since we may need to use the grid information before constructing
153  // initial idealized data
154  // ********************************************************************************************
155  if (restart_chkfile.empty()) {
156  if ( (solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid) )
157  {
158  AMREX_ALWAYS_ASSERT(solverChoice.terrain_type == TerrainType::StaticFittedMesh);
159  //
160  // Note that "time" here is elapsed time, and start_time is the start_time from wrfinput/metgrid files
161  //
162  if (nc_init_file[lev].empty()) {
163  //
164  // A refined level with no file of its own has no PH/PHB to build terrain
165  // from, so its terrain must be interpolated from the coarser level FIRST --
166  // the base state init_only builds is a function of the cell-centered heights
167  // and cannot run before they exist. (init_zphys interpolates from coarse
168  // exactly when this level has no file, which is the same condition.)
169  //
170  init_zphys(lev, time);
172  make_physbcs(lev);
173  init_only(lev, time);
174  } else {
175  //
176  // A level that does have a file reads its terrain and its data in the same
177  // pass, so init_only must come first here.
178  //
179  // Check if user requested interp_atmos_from_coarse but we're in MakeNewLevelFromScratch.
180  // In this case, both levels are starting at the same time, so the atmospheric states
181  // in the files should already be consistent. We ignore the flag and read from the file.
182  if (solverChoice.interp_atmos_from_coarse && lev > 0 &&
183  solverChoice.init_type == InitType::WRFInput) {
184  if (ParallelDescriptor::IOProcessor()) {
185  amrex::Warning("erf.interp_atmos_from_coarse = true is set, but both levels are starting "
186  "from scratch at the same time. The atmospheric state at level " + std::to_string(lev) +
187  " will be read from the wrfinput file instead of interpolated from coarse. "
188  "This option is intended for time-mismatched WRF input files or regridding, "
189  "not for initial startup with consistent files.");
190  }
191  }
192  init_only(lev, time);
193  init_zphys(lev, time);
195  make_physbcs(lev);
196  }
197  } else {
198  //
199  // Note that "time" here is elapsed time, and start_time = 0 when not using wrfinput/metgrid
200  //
201  init_zphys(lev, time);
203  // Note that for init_type != InitType::WRFInput and != InitType::Metgrid,
204  // make_physbcs is called inside init_only
205  init_only(lev, time);
206  }
207  } else {
208  // If restarting from an input-sounding run, load the sounding again. It is
209  // needed for nudging, and InitData_post also takes the surface temperature
210  // and moisture of the surface layer from its reference values; without this
211  // read those are zero on a restart, the surface layer sees a 0 K surface,
212  // and the restarted run drifts from the uninterrupted one.
213  if (lev == 0 && solverChoice.init_type == InitType::Input_Sounding)
214  {
216  Error("input_sounding file name must be provided via input");
217  }
218 
220 
221  // this will interpolate the input profiles to the nominal height levels
222  // (ranging from 0 to the domain top)
223  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
224  for (int n = 0; n < input_sounding_data.n_sounding_files; n++) {
225  input_sounding_data.read_from_file(geom[lev], zlevels_stag[lev], n, is_moist);
226  }
227 
229 
230  // this will calculate the hydrostatically balanced density and pressure
231  // profiles following WRF ideal.exe
232  if (solverChoice.sounding_type == SoundingType::Ideal) {
234  } else if (solverChoice.sounding_type == SoundingType::Isentropic ||
235  solverChoice.sounding_type == SoundingType::DryIsentropic) {
236  input_sounding_data.assume_dry = (solverChoice.sounding_type == SoundingType::DryIsentropic);
238  }
239  }
240 
241  // We re-create terrain_blanking on restart rather than storing it in the checkpoint
242  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
243  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
244  int ngrow = ComputeGhostCells(solverChoice) + 2;
245  terrain_blanking[lev]->setVal(1.0);
246  MultiFab::Subtract(*terrain_blanking[lev], EBFactory(lev).getVolFrac(), 0, 0, 1, ngrow);
247  terrain_blanking[lev]->FillBoundary(geom[lev].periodicity());
248  }
249  }
250 
251  // Read in tables needed for windfarm simulations
252  // fill in Nturb multifab - number of turbines in each mesh cell
253  // write out the vtk files for wind turbine location and/or
254  // actuator disks
255  #ifdef ERF_USE_WINDFARM
256  init_windfarm(lev);
257  #endif
258 
259  // ********************************************************************************************
260  // Build the data structures for canopy model (depends upon z_phys)
261  // ********************************************************************************************
262  if (restart_chkfile.empty()) {
264  m_forest_drag[lev]->define_drag_field(ba, dm, geom[lev],
265  z_phys_cc[lev].get(), z_phys_nd[lev].get(),
268  }
269  }
270 
271  //********************************************************************************************
272  // Microphysics
273  // *******************************************************************************************
274  int q_size = micro->Get_Qmoist_Size(lev);
275  qmoist[lev].resize(q_size);
276  micro->Define(lev, solverChoice);
277  if (solverChoice.moisture_type != MoistureType::None)
278  {
279  micro->Init(lev, vars_new[lev][Vars::cons],
280  grids[lev], Geom(lev), zero,
281  z_phys_nd[lev], detJ_cc[lev]); // dummy dt value
282  // Refresh the land/water mask pointer. Must be re-issued at every
283  // micro->Init site: init_stuff rebuilds lmask_lev[lev][0], so a stale
284  // pointer would dangle. No-op for every scheme except WDM6.
285  micro->Set_Lmask(lev, (lmask_lev[lev].empty()) ? nullptr : lmask_lev[lev][0].get());
286  }
287  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
288  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
289  }
290 
291  //********************************************************************************************
292  // Radiation
293  // *******************************************************************************************
295  {
296  rad[lev]->Init(geom[lev], ba, &vars_new[lev][Vars::cons]);
297  }
298 
299  // ********************************************************************************************
300  // If we are making a new level then the FillPatcher for this level hasn't been allocated yet
301  // ********************************************************************************************
302  if (lev > 0 && cf_width >= 0) {
305  }
306 
307 #ifdef ERF_USE_PARTICLES
308  if (restart_chkfile.empty()) {
309  if (lev == 0) {
310  initializeTracers((ParGDBBase*)GetParGDB(),z_phys_nd,time);
311  }
312  // For lev > 0: particle redistribute is handled in timeStep() AFTER
313  // regrid() completes, not here inside MakeNewLevelFromCoarse.
314  }
315 #endif
316 }
BoxArray ERFPostProcessBaseGrids(const Box &domain, bool decompose_in_z)
Definition: ERF_ChopGrids.cpp:13
void init_only(int lev, double time)
Definition: ERF.cpp:2425
void init_thin_body(int lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
Definition: ERF_MakeNewLevel.cpp:1335
void set_start_time(amrex::Real start_time)
Definition: ERF_InputSoundingData.H:482
Here is the call graph for this function:

◆ MakeVTKFilename()

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

◆ MakeVTKFilename_EyeTracker_xy()

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

◆ MakeVTKFilename_TrackerCircle()

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

◆ nghost_eb_basic()

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

◆ nghost_eb_full()

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

◆ nghost_eb_volume()

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

◆ NumDataLogs()

AMREX_FORCE_INLINE int ERF::NumDataLogs ( )
inlineprivatenoexcept
1959  {
1960  return static_cast<int>(datalog.size());
1961  }

◆ NumDerDataLogs()

AMREX_FORCE_INLINE int ERF::NumDerDataLogs ( )
inlineprivatenoexcept
1966  {
1967  return static_cast<int>(der_datalog.size());
1968  }

◆ NumSampleLineLogs()

AMREX_FORCE_INLINE int ERF::NumSampleLineLogs ( )
inlineprivatenoexcept
1995  {
1996  return static_cast<int>(samplelinelog.size());
1997  }

◆ NumSampleLines()

AMREX_FORCE_INLINE int ERF::NumSampleLines ( )
inlineprivatenoexcept
2021  {
2022  return static_cast<int>(sampleline.size());
2023  }

◆ NumSamplePointLogs()

AMREX_FORCE_INLINE int ERF::NumSamplePointLogs ( )
inlineprivatenoexcept
1981  {
1982  return static_cast<int>(sampleptlog.size());
1983  }

◆ NumSamplePoints()

AMREX_FORCE_INLINE int ERF::NumSamplePoints ( )
inlineprivatenoexcept
2008  {
2009  return static_cast<int>(samplepoint.size());
2010  }

◆ 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,
double  time,
const amrex::Vector< const amrex::MultiFab * > &  weight_mf,
const amrex::Vector< const amrex::iMultiFab * > &  index_mf,
int  max_stencil_size,
const amrex::MultiFab *  dst_mskr = nullptr,
const amrex::MultiFab *  dst_msku = nullptr,
const amrex::MultiFab *  dst_mskv = nullptr 
)
378 {
379  using namespace amrex;
380 
381  // WeightFamily slot order, mirrored from ERFRemoraMultiBlockContainer.H:
382  // {SCALAR_CENTERED, TAUX_FACE, TAUY_FACE, WIND_FACE_TO_CENTER}.
383  constexpr int kScalarFamily = 0, kTauXFamily = 1, kTauYFamily = 2, kWindFamily = 3;
385  weight_mf_by_family.size() == 4 && index_mf_by_family.size() == 4,
386  "PackAtmosphericStates requires 4 WeightFamily-keyed weight/index pairs.");
387  for (int f = 0; f < 4; ++f) {
389  weight_mf_by_family[f] != nullptr && index_mf_by_family[f] != nullptr,
390  "PackAtmosphericStates requires valid driver remap stencil components for every WeightFamily slot.");
391  // The stencil loops read max_stencil_size weights and 3*max_stencil_size
392  // index components. Nothing else checks that the driver allocated that
393  // many, so a driver/submodule version skew would read past the component
394  // count and silently return garbage rather than failing.
396  weight_mf_by_family[f]->nComp() >= max_stencil_size &&
397  index_mf_by_family[f]->nComp() >= 3 * max_stencil_size,
398  "PackAtmosphericStates: driver remap stencil arrays have too few components for max_stencil_size.");
400  weight_mf_by_family[f]->boxArray() == index_mf_by_family[f]->boxArray() &&
401  weight_mf_by_family[f]->DistributionMap() == index_mf_by_family[f]->DistributionMap(),
402  "PackAtmosphericStates: weight and index arrays for a WeightFamily slot disagree on layout.");
403  }
404  const amrex::MultiFab* weight_mf = weight_mf_by_family[kScalarFamily];
405  const amrex::iMultiFab* index_mf = index_mf_by_family[kScalarFamily];
406 
407  // Values for destination cells no source cell overlaps (see
408  // ApplyConservativeRemap). These are standard-atmosphere placeholders, not
409  // physics: they exist so the receiving model's flux formulas stay in their
410  // valid domain over cells the atmosphere grid never covered. Wind is the one
411  // field where zero is genuinely meaningful (calm), so it needs no special
412  // value. Units are the driver contract's: Pa and K, converted downstream.
413  constexpr Real fallback_pressure_pa = Real(101325.0); // sea-level standard
414  constexpr Real fallback_temp_k = Real(288.15); // 15 C, standard
415  constexpr Real fallback_wind_ms = Real(0.0); // calm
416 
417  const int lev = 0;
418 
419  auto& cons = vars_new[lev][Vars::cons];
420  auto& xvel = vars_new[lev][Vars::xvel]; // XFace
421  auto& yvel = vars_new[lev][Vars::yvel]; // YFace
422 
423  const bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
424  // Level 0 only, matching HasRadiation(); lev is 0 throughout this routine.
425  const bool has_radiation = HasRadiation();
426 
427  amrex::ignore_unused(has_moisture, has_radiation);
428 
429  const auto& ba = cons.boxArray();
430  const auto& dm = cons.DistributionMap();
431  const auto& ba2d_lev = ba2d[lev];
432  const auto& xba2d_lev = amrex::convert(ba2d_lev, IntVect(1,0,0));
433  const auto& yba2d_lev = amrex::convert(ba2d_lev, IntVect(0,1,0));
434  const int klo = ba.minimalBox().smallEnd(2);
435 
436  const bool flux_mode = (states.size() == nFluxLanes);
437  if (flux_mode) {
438  if (verbose) {
439  amrex::Print() << "ERF flux-pack: states.size()=" << states.size()
440  << " has_radiation=" << has_radiation
441  << " tau13_ptr=" << (Tau[lev][TauType::tau13] != nullptr)
442  << " tau23_ptr=" << (Tau[lev][TauType::tau23] != nullptr)
443  << " sfs_hfx3_ptr="
444  << (!SFS_hfx3_lev.empty() && SFS_hfx3_lev[lev] != nullptr)
445  << " sfs_q1fx3_ptr="
446  << (!SFS_q1fx3_lev.empty() && SFS_q1fx3_lev[lev] != nullptr)
447  << " rad_fluxes_ptr="
448  << (!rad_fluxes.empty() && rad_fluxes[lev] != nullptr)
449  << "\n";
450  if (Tau[lev][TauType::tau13] != nullptr) {
451  PrintFluxLaneStats("tau13_src", *Tau[lev][TauType::tau13]);
452  }
453  if (Tau[lev][TauType::tau23] != nullptr) {
454  PrintFluxLaneStats("tau23_src", *Tau[lev][TauType::tau23]);
455  }
456  if (!SFS_hfx3_lev.empty() && SFS_hfx3_lev[lev] != nullptr) {
457  PrintFluxLaneStats("SFS_hfx3_src", *SFS_hfx3_lev[lev]);
458  }
459  if (!SFS_q1fx3_lev.empty() && SFS_q1fx3_lev[lev] != nullptr) {
460  PrintFluxLaneStats("SFS_q1fx3_src", *SFS_q1fx3_lev[lev]);
461  }
462  if (has_radiation) {
463  PrintFluxLaneStats("rad_fluxes_swup_src", *rad_fluxes[lev], 0);
464  PrintFluxLaneStats("rad_fluxes_swdn_src", *rad_fluxes[lev], 1);
465  PrintFluxLaneStats("rad_fluxes_lwup_src", *rad_fluxes[lev], 2);
466  PrintFluxLaneStats("rad_fluxes_lwdn_src", *rad_fluxes[lev], 3);
467  }
468  }
469 
470  //
471  // NOTE: Tau tau13/tau23 already hold CONSERVATIVE stress in [N m-2] --
472  // compute_u_flux in ERF_MOSTStress.H returns rho*<u'w'>, and
473  // ComputeStress_*_N/T scale the strain by rho_bar*mu_eff or by
474  // mu_turb (= rho*K). They must therefore be exported as-is,
475  // without another factor of density. This also matches the
476  // SH/LH lanes below, which apply only Cp_d / L_v.
477  //
478  // NOTE: Tau is only allocated when diffusion is active, so the pointers
479  // have to be checked before they are dereferenced.
480  //
481 
482  // TAU LANE STAGGER: unresolved, see the parked block below.
483  //
484  // ERF tau13/tau23 live on x-/y-faces and REMORA vec_sustr/vec_svstr live on
485  // u-/v-faces, so the physically meaningful transfer is face -> face: a
486  // stagger-matched transfer with no averaging on a conformal grid. That is
487  // what the live code below does, and what the driver's WeightFamily comment
488  // describes.
489  //
490  // The defect is on the weight side, not here: the driver builds its remap
491  // stencils from source *cell* polygons and stores ERF *cell* indices for
492  // every family (ERFRemoraMultiBlockContainer.cpp, the ExtractCellQuadFromCoords
493  // / CellBoundsFromGeom calls in BuildAtmosToOceanWeights). So the index this
494  // face-staggered source is read with was computed for a cell, displacing tau
495  // by ~dx/2 and leaving the outermost face column unreachable.
496  //
497  // The parked block below "fixed" that by averaging tau to cell centres so the
498  // source would match the cell-based weights. That is the wrong direction: it
499  // smooths the stress twice (face -> cell, then cell -> face via area weights)
500  // and throws away the face alignment. Keeping it only as a record of the
501  // attempt. The correct fix is to build ERF *face* control-volume source
502  // polygons for the TAUX/TAUY families in the driver, so the weights match the
503  // face source that is already being passed here.
504 #if 0
505  if (iTauX < static_cast<int>(states.size()) && states[iTauX] != nullptr) {
506  MultiFab tmp(ba2d_lev, dm, 1, 0);
507  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
508  Box bx = mfi.tilebox();
509  auto const& tau13 = Tau[lev][TauType::tau13]->const_array(mfi);
510  auto const& c = cons.const_array(mfi);
511  auto t = tmp.array(mfi);
512  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
513  t(i,j,k) = c(i,j,klo,Rho_comp)
514  * Real(0.5) * (tau13(i,j,klo) + tau13(i+1,j,klo));
515  });
516  }
517  ApplyConservativeRemap(tmp, *states[iTauX], *weight_mf_by_family[kTauXFamily],
518  *index_mf_by_family[kTauXFamily], max_stencil_size, dst_msku);
519  }
520  if (iTauY < static_cast<int>(states.size()) && states[iTauY] != nullptr) {
521  MultiFab tmp(ba2d_lev, dm, 1, 0);
522  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
523  Box bx = mfi.tilebox();
524  auto const& tau23 = Tau[lev][TauType::tau23]->const_array(mfi);
525  auto const& c = cons.const_array(mfi);
526  auto t = tmp.array(mfi);
527  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
528  t(i,j,k) = c(i,j,klo,Rho_comp)
529  * Real(0.5) * (tau23(i,j,klo) + tau23(i,j+1,klo));
530  });
531  }
532  ApplyConservativeRemap(tmp, *states[iTauY], *weight_mf_by_family[kTauYFamily],
533  *index_mf_by_family[kTauYFamily], max_stencil_size, dst_mskv);
534  }
535 #endif
536 
537  if (iTauX < static_cast<int>(states.size()) && states[iTauX] != nullptr) {
539  "Flux-mode coupling of tau_x requires Tau; enable diffusion or a surface_layer bc");
540  MultiFab tmp(xba2d_lev, dm, 1, 0);
541  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
542  Box bx = mfi.tilebox();
543  auto const& tau13 = Tau[lev][TauType::tau13]->const_array(mfi);
544  auto t = tmp.array(mfi);
545  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
546  t(i,j,k) = tau13(i,j,klo);
547  });
548  }
549  ApplyConservativeRemap(tmp, *states[iTauX], *weight_mf_by_family[kTauXFamily],
550  *index_mf_by_family[kTauXFamily], max_stencil_size, dst_msku);
551  if (verbose) { PrintFluxLaneStats("tau_x_lane", *states[iTauX]); }
552  }
553 
554  if (iTauY < static_cast<int>(states.size()) && states[iTauY] != nullptr) {
556  "Flux-mode coupling of tau_y requires Tau; enable diffusion or a surface_layer bc");
557  MultiFab tmp(yba2d_lev, dm, 1, 0);
558  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
559  Box bx = mfi.tilebox();
560  auto const& tau23 = Tau[lev][TauType::tau23]->const_array(mfi);
561  auto t = tmp.array(mfi);
562  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
563  t(i,j,k) = tau23(i,j,klo);
564  });
565  }
566  ApplyConservativeRemap(tmp, *states[iTauY], *weight_mf_by_family[kTauYFamily],
567  *index_mf_by_family[kTauYFamily], max_stencil_size, dst_mskv);
568  if (verbose) { PrintFluxLaneStats("tau_y_lane", *states[iTauY]); }
569  }
570 
571  if (iSHflux < static_cast<int>(states.size()) && states[iSHflux] != nullptr &&
572  !SFS_hfx3_lev.empty() && SFS_hfx3_lev[lev] != nullptr) {
573  MultiFab tmp(ba2d_lev, dm, 1, 0);
574  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
575  Box bx = mfi.tilebox();
576  auto const& hfx = SFS_hfx3_lev[lev]->const_array(mfi);
577  auto t = tmp.array(mfi);
578  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
580  hfx(i,j,klo));
581  });
582  }
583  ApplyConservativeRemap(tmp, *states[iSHflux], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
584  if (verbose) { PrintFluxLaneStats("SHflux_lane", *states[iSHflux]); }
585  }
586 
587  if (iLHflux < static_cast<int>(states.size()) && states[iLHflux] != nullptr &&
588  !SFS_q1fx3_lev.empty() && SFS_q1fx3_lev[lev] != nullptr) {
589  MultiFab tmp(ba2d_lev, dm, 1, 0);
590  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
591  Box bx = mfi.tilebox();
592  auto const& qfx = SFS_q1fx3_lev[lev]->const_array(mfi);
593  auto t = tmp.array(mfi);
594  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
596  qfx(i,j,klo));
597  });
598  }
599  ApplyConservativeRemap(tmp, *states[iLHflux], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
600  if (verbose) { PrintFluxLaneStats("LHflux_lane", *states[iLHflux]); }
601  }
602 
603  if (has_radiation) {
604  if (iFluxSWrad < static_cast<int>(states.size()) && states[iFluxSWrad] != nullptr) {
605  MultiFab tmp(ba2d_lev, dm, 1, 0);
606  tmp.ParallelCopy(*rad_fluxes[lev], 1, 0, 1);
607  ApplyConservativeRemap(tmp, *states[iFluxSWrad], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
608  if (verbose) { PrintFluxLaneStats("SWrad_lane", *states[iFluxSWrad]); }
609  }
610  if (iFluxLWrad < static_cast<int>(states.size()) && states[iFluxLWrad] != nullptr) {
611  MultiFab tmp(ba2d_lev, dm, 1, 0);
612  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
613  Box bx = mfi.tilebox();
614  auto const& rad_flux = rad_fluxes[lev]->const_array(mfi);
615  auto t = tmp.array(mfi);
616  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
617  t(i,j,k) = rad_flux(i,j,k,3) - rad_flux(i,j,k,2);
618  });
619  }
620  ApplyConservativeRemap(tmp, *states[iFluxLWrad], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
621  if (verbose) { PrintFluxLaneStats("LWrad_lane", *states[iFluxLWrad]); }
622  }
623  }
624 
625  if (iFluxEvap < static_cast<int>(states.size()) && states[iFluxEvap] != nullptr &&
626  !SFS_q1fx3_lev.empty() && SFS_q1fx3_lev[lev] != nullptr) {
627  MultiFab tmp(ba2d_lev, dm, 1, 0);
628  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
629  Box bx = mfi.tilebox();
630  auto const& qfx = SFS_q1fx3_lev[lev]->const_array(mfi);
631  auto t = tmp.array(mfi);
632  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
633  t(i,j,k) = qfx(i,j,klo);
634  });
635  }
636  ApplyConservativeRemap(tmp, *states[iFluxEvap], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
637  if (verbose) { PrintFluxLaneStats("evap_lane", *states[iFluxEvap]); }
638  }
639 
640 #if 0
641  // Extract Rain Flux using surface qr and a proxy terminal velocity
642  if (has_moisture && iFluxRain < static_cast<int>(states.size()) && states[iFluxRain] != nullptr) {
643  int qr_idx = solverChoice.moisture_indices.qr;
644  if (qr_idx != -1) {
645  MultiFab tmp(ba2d_lev, dm, 1, 0);
646  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
647  Box bx = mfi.tilebox();
648  auto const& c = cons.const_array(mfi);
649  auto t = tmp.array(mfi);
650  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
651  // Rain flux proxy: (rho * qr) * V_terminal (approx 5.0 m/s for rain) -> kg m^-2 s^-1
652  t(i,j,k) = c(i,j,klo,qr_idx) * Real(5.0);
653  });
654  }
655  ApplyConservativeRemap(tmp, *states[iFluxRain], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
656  if (verbose) { PrintFluxLaneStats("rain_lane", *states[iFluxRain]); }
657  }
658  }
659 #endif
660  amrex::ignore_unused(iFluxRain);
661  return;
662  }
663 
664  // --- Uwind + Vwind ---
665  if ((iUwind < static_cast<int>(states.size()) && states[iUwind] != nullptr) ||
666  (iVwind < static_cast<int>(states.size()) && states[iVwind] != nullptr)) {
667 
668  auto& zvel = vars_new[lev][Vars::zvel];
669  MultiFab cc_vel(ba, dm, AMREX_SPACEDIM, 0);
670  amrex::average_face_to_cellcenter(cc_vel, 0,
671  Array<const MultiFab*, AMREX_SPACEDIM>{&xvel, &yvel, &zvel});
672 
673  // Collapse to 2D slab
674  MultiFab uv_slab(ba2d_lev, dm, 2, 0); // comp0=u, comp1=v
675  uv_slab.ParallelCopy(cc_vel, 0, 0, 2);
676 
677  if (iUwind < static_cast<int>(states.size()) && states[iUwind] != nullptr) {
678  MultiFab u_alias(uv_slab, amrex::make_alias, 0, 1); // alias u component
679  ApplyConservativeRemap(u_alias, *states[iUwind], *weight_mf_by_family[kWindFamily],
680  *index_mf_by_family[kWindFamily], max_stencil_size,
681  nullptr, fallback_wind_ms);
682  }
683 
684  if (iVwind < static_cast<int>(states.size()) && states[iVwind] != nullptr) {
685  MultiFab v_alias(uv_slab, amrex::make_alias, 1, 1); // alias v component
686  ApplyConservativeRemap(v_alias, *states[iVwind], *weight_mf_by_family[kWindFamily],
687  *index_mf_by_family[kWindFamily], max_stencil_size,
688  nullptr, fallback_wind_ms);
689  }
690  }
691 
692  // --- Patm ---
693  if (iPatm < static_cast<int>(states.size()) && states[iPatm] != nullptr) {
694  MultiFab tmp(ba2d_lev, dm, 1, 0);
695  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
696  Box bx = mfi.tilebox();
697  auto const& c = cons.const_array(mfi);
698  auto t = tmp.array(mfi);
699  if (has_moisture) {
700  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
701  const Real qv = c(i,j,k,RhoQ1_comp) / c(i,j,k,Rho_comp);
702  t(i,j,k) = getPgivenRTh(c(i,j,k,RhoTheta_comp), qv);
703  });
704  } else {
705  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
706  t(i,j,k) = getPgivenRTh(c(i,j,k,RhoTheta_comp));
707  });
708  }
709  }
710  ApplyConservativeRemap(tmp, *states[iPatm], *weight_mf, *index_mf, max_stencil_size, dst_mskr,
711  fallback_pressure_pa);
712  }
713 
714  // --- Tair ---
715  if (iTair < static_cast<int>(states.size()) && states[iTair] != nullptr) {
716  MultiFab tmp(ba2d_lev, dm, 1, 0);
717  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
718  Box bx = mfi.tilebox();
719  auto const& c = cons.const_array(mfi);
720  auto t = tmp.array(mfi);
721  if (has_moisture) {
722  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
723  const Real qv = c(i,j,k,RhoQ1_comp) / c(i,j,k,Rho_comp);
724  t(i,j,k) = getTgivenRandRTh(c(i,j,k,Rho_comp), c(i,j,k,RhoTheta_comp), qv);
725  });
726  } else {
727  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
728  t(i,j,k) = getTgivenRandRTh(c(i,j,k,Rho_comp), c(i,j,k,RhoTheta_comp));
729  });
730  }
731  }
732  ApplyConservativeRemap(tmp, *states[iTair], *weight_mf, *index_mf, max_stencil_size, dst_mskr,
733  fallback_temp_k);
734  }
735 
736  // --- Humidity/Cloud/Rain ---
737  if (has_moisture) {
738 #if 0
739  if (iRH < static_cast<int>(states.size()) && states[iRH] != nullptr) {
740  MultiFab tmp(ba2d_lev, dm, 1, 0);
741  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
742  Box bx = mfi.tilebox();
743  auto const& c = cons.const_array(mfi);
744  auto t = tmp.array(mfi);
745  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
746  const Real qv = c(i,j,k,RhoQ1_comp) / c(i,j,k,Rho_comp);
747  const Real p_pa = getPgivenRTh(c(i,j,k,RhoTheta_comp), qv);
748  const Real temp = getTgivenRandRTh(c(i,j,k,Rho_comp), c(i,j,k,RhoTheta_comp), qv);
749  Real qsat = Real(0.0);
750  erf_qsatw(temp, p_pa * Real(0.01), qsat);
751  t(i,j,k) = amrex::max(Real(0.0), amrex::min(Real(1.0),
752  qv / amrex::max(qsat, Real(1.0e-12))));
753  });
754  }
755  IntVect ratio = ba2d_lev.minimalBox().length() / states[iRH]->boxArray().minimalBox().length();
756  amrex::average_down(tmp, *states[iRH], 0, 1, ratio);
757  }
758 #else
759  amrex::ignore_unused(iRH);
760  // Avoiding unexercised moist assumptions: retain the driver-prefilled
761  // humidity lane until live ERF->REMORA Qair semantics are made explicit.
762 #endif
763  if (iCloud < static_cast<int>(states.size()) && states[iCloud] != nullptr) {
764  const int qc_idx = solverChoice.moisture_indices.qc;
765  const int qi_idx = solverChoice.moisture_indices.qi;
766  if (qc_idx != -1 || qi_idx != -1) {
767  MultiFab tmp(ba2d_lev, dm, 1, 0);
768  const Real cf = amrex::max(Real(0.0), amrex::min(Real(1.0), cloud_fraction(0.0)));
769  amrex::ignore_unused(cf); // keep diagnostic computation active for consistency with ERF scalar stats
770  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
771  Box bx = mfi.tilebox();
772  auto const& c = cons.const_array(mfi);
773  auto t = tmp.array(mfi);
774  const int khi = ba.minimalBox().bigEnd(2);
775  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
776  int cloudy = 0;
777  for (int kk = klo; kk <= khi; ++kk) {
778  const Real rho = c(i,j,kk,Rho_comp);
779  const Real qc = (qc_idx != -1) ? c(i,j,kk,qc_idx) / rho : Real(0.0);
780  const Real qi = (qi_idx != -1) ? c(i,j,kk,qi_idx) / rho : Real(0.0);
781  if (qc + qi > Real(0.0)) { cloudy = 1; break; }
782  }
783  t(i,j,k) = static_cast<Real>(cloudy);
784  });
785  }
786  ApplyConservativeRemap(tmp, *states[iCloud], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
787  }
788  }
789 #if 0
790  if (iRain < static_cast<int>(states.size()) && states[iRain] != nullptr) {
791  int qr_idx = solverChoice.moisture_indices.qr;
792  if (qr_idx != -1) {
793  MultiFab tmp(ba2d_lev, dm, 1, 0);
794  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
795  Box bx = mfi.tilebox();
796  auto const& c = cons.const_array(mfi);
797  auto t = tmp.array(mfi);
798  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
799  t(i,j,k) = c(i,j,k,qr_idx) / c(i,j,k,Rho_comp);
800  });
801  }
802  IntVect ratio = ba2d_lev.minimalBox().length() / states[iRain]->boxArray().minimalBox().length();
803  amrex::average_down(tmp, *states[iRain], 0, 1, ratio);
804  }
805  }
806 #else
807  amrex::ignore_unused(iRain);
808  // Avoiding unexercised moist assumptions: retain the driver-prefilled
809  // rain lane until live ERF->REMORA rain semantics are made explicit.
810 #endif
811  }
812  // No moisture: leave RH/Cloud/Rain slabs at their driver-pre-filled values.
813 
814  // --- Radiation ---
815  if (has_radiation) {
816  if (iSWrad < static_cast<int>(states.size()) && states[iSWrad] != nullptr) {
817  MultiFab tmp(ba2d_lev, dm, 1, 0);
818  tmp.ParallelCopy(*rad_fluxes[lev], 1, 0, 1);
819  ApplyConservativeRemap(tmp, *states[iSWrad], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
820  }
821  if (iLWrad < static_cast<int>(states.size()) && states[iLWrad] != nullptr) {
822  MultiFab tmp(ba2d_lev, dm, 1, 0);
823  tmp.ParallelCopy(*rad_fluxes[lev], 3, 0, 1);
824  ApplyConservativeRemap(tmp, *states[iLWrad], *weight_mf, *index_mf, max_stencil_size, dst_mskr);
825  }
826  }
827 }
amrex::Real cloud_fraction(double time)
Definition: ERF_WriteScalarProfiles.cpp:458
@ qi
Definition: ERF_WDM6.H:28
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sensible_heat_flux_wm2_from_rhotheta_flux(amrex::Real rhotheta_flux) noexcept
Definition: ERF_SurfaceFluxDiagnostics.H:29
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real latent_heat_flux_wm2_from_rhoqv_flux(amrex::Real rhoqv_flux) noexcept
Definition: ERF_SurfaceFluxDiagnostics.H:36
Here is the call graph for this function:

◆ ParameterSanityChecks()

void ERF::ParameterSanityChecks ( )
private
3285 {
3287 
3288  // We don't allow use_real_bcs to be true if init_type is not either InitType::WRFInput or InitType::Metgrid
3290  ((solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid)) );
3291 
3293  if (!solverChoice.use_real_bcs || solverChoice.init_type != InitType::WRFInput) {
3294  Abort("erf.use_wrf_bdy_density requires standard WRFInput real boundary conditions");
3295  }
3296  if (nc_bdy_file.empty()) {
3297  Abort("erf.use_wrf_bdy_density requires nc_bdy_file");
3298  }
3299  if (!solverChoice.anelastic.empty() && solverChoice.anelastic[0] != 0) {
3300  Abort("erf.use_wrf_bdy_density is not supported for anelastic simulations");
3301  }
3302  if (input_bndry_planes) {
3303  Abort("erf.use_wrf_bdy_density is not supported with generic boundary-plane input");
3304  }
3305  const Real rho_factor = (solverChoice.bdy_rho_nudge_factor > zero)
3308  if (rho_factor <= zero) {
3309  Abort("erf.bdy_rho_nudge_factor or erf.bdy_nudge_factor must be positive");
3310  }
3311  Print() << "WRF lateral boundary density forcing: enabled\n"
3312  << "WRF boundary density nudge factor: " << rho_factor << std::endl;
3313  } else {
3314  Print() << "WRF lateral boundary density forcing: disabled" << std::endl;
3315  }
3316 
3318  if (!solverChoice.use_real_bcs || solverChoice.init_type != InitType::WRFInput) {
3319  Abort("erf.use_wrf_bdy_qc_qi requires standard WRFInput real boundary conditions");
3320  }
3321  if (nc_bdy_file.empty()) {
3322  Abort("erf.use_wrf_bdy_qc_qi requires erf.nc_bdy_file");
3323  }
3324  if (input_bndry_planes) {
3325  Abort("erf.use_wrf_bdy_qc_qi is not supported with generic boundary-plane input");
3326  }
3327  Print() << "WRF lateral boundary cloud-water/cloud-ice forcing: enabled" << std::endl;
3328  } else {
3329  Print() << "WRF lateral boundary cloud-water/cloud-ice forcing: disabled" << std::endl;
3330  }
3331 
3333 
3334  if (cf_set_width != 0) {
3335  Abort("You must set cf_set_width == 0");
3336  }
3337  if (cf_width < 0 || cf_set_width < 0 || cf_width < cf_set_width) {
3338  Abort("You must set cf_width >= cf_set_width >= 0");
3339  }
3340  if (max_level > 0 && cf_set_width > 0) {
3341  for (int lev = 1; lev <= max_level; lev++) {
3342  if (cf_set_width%ref_ratio[lev-1][0] != 0 ||
3343  cf_set_width%ref_ratio[lev-1][1] != 0 ||
3344  cf_set_width%ref_ratio[lev-1][2] != 0 ) {
3345  Abort("You must set cf_width to be a multiple of ref_ratio");
3346  }
3347  }
3348  }
3349 
3350  // If fixed_mri_dt_ratio is set, it must be even
3351  if (fixed_mri_dt_ratio > 0 && (fixed_mri_dt_ratio%2 != 0) )
3352  {
3353  Abort("If you specify fixed_mri_dt_ratio, it must be even");
3354  }
3355 
3356  for (int lev = 0; lev <= max_level; lev++)
3357  {
3358  // We ignore fixed_fast_dt if not substepping
3359  if (solverChoice.substepping_type[lev] == SubsteppingType::None) {
3360  fixed_fast_dt[lev] = -one;
3361  }
3362 
3363  // If both fixed_dt and fast_dt are specified, their ratio must be an even integer
3364  if (fixed_dt[lev] > zero && fixed_fast_dt[lev] > zero && fixed_mri_dt_ratio <= 0)
3365  {
3366  Real eps = Real(1.e-12);
3367  int ratio = static_cast<int>( ( (one+eps) * fixed_dt[lev] ) / fixed_fast_dt[lev] );
3368  if (fixed_dt[lev] / fixed_fast_dt[lev] != ratio)
3369  {
3370  Abort("Ratio of fixed_dt to fixed_fast_dt must be an even integer");
3371  }
3372  }
3373 
3374  // If all three are specified, they must be consistent
3375  if (fixed_dt[lev] > zero && fixed_fast_dt[lev] > zero && fixed_mri_dt_ratio > 0)
3376  {
3377  if (fixed_dt[lev] / fixed_fast_dt[lev] != fixed_mri_dt_ratio)
3378  {
3379  Abort("Dt is over-specfied");
3380  }
3381  }
3382  } // lev
3383 
3384  if (solverChoice.coupling_type == CouplingType::TwoWay && cf_width > 0) {
3385  Abort("For two-way coupling you must set cf_width = 0");
3386  }
3387 }
if(l_use_mynn &&start_comp<=RhoKE_comp &&end_comp >=RhoKE_comp)
Definition: ERF_AddQKESources.H:2
bool use_wrf_bdy_density
Definition: ERF_DataStruct.H:2278
amrex::Real bdy_nudge_factor
Boundary sponge nudging factor.
Definition: ERF_DataStruct.H:2275
amrex::Real bdy_rho_nudge_factor
Definition: ERF_DataStruct.H:2284
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
277 {
278  Vector<std::string> names;
279 
280  names.insert(names.end(), plot_var_names.begin(), plot_var_names.end());
281 
282  return names;
283 
284 }

◆ 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

Parameters
levLevel index for the wall-distance solve
29 {
30  BL_PROFILE("ERF::poisson_wall_dist()");
31 
32  bool havewall{false};
33  Orientation zlo(Direction::z, Orientation::low);
34  if ( ( phys_bc_type[zlo] == ERF_BC::surface_layer ) ||
35  ( phys_bc_type[zlo] == ERF_BC::no_slip_wall ) )/*||
36  ((phys_bc_type[zlo] == ERF_BC::slip_wall) && (dom_hi.z > dom_lo.z)) )*/
37  {
38  havewall = true;
39  }
40 
41  auto const& geomdata = geom[lev];
42  auto const& dxinv = geomdata.InvCellSizeArray();
43 
44  auto const& zphys_arr = z_phys_nd[lev]->const_arrays();
45 
46  if (havewall) {
47 #if 1
48  // Bypass wall dist calc in the trivial cases
49 
50  if (solverChoice.mesh_type == MeshType::ConstantDz) {
51  Print() << "Directly calculating direct wall distance for constant dz" << std::endl;
52  const auto prob_lo = geomdata.ProbLoArray();
53  const auto dx = geomdata.CellSizeArray();
54  for (MFIter mfi(*walldist[lev]); mfi.isValid(); ++mfi) {
55  const Box& bx = mfi.validbox();
56  auto dist_arr = walldist[lev]->array(mfi);
57  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
58  dist_arr(i, j, k) = prob_lo[2] + (k + myhalf) * dx[2];
59  });
60  }
61  fill_wall_dist_ghost_cells(*walldist[lev], geom[lev]);
62  return;
63  }
64 
65  if (solverChoice.mesh_type == MeshType::StretchedDz) {
66  Print() << "Directly calculating direct wall distance for stretched dz" << std::endl;
67  for (MFIter mfi(*walldist[lev],TileNoZ()); mfi.isValid(); ++mfi) {
68  const Box& bx = mfi.validbox();
69  auto dist_arr = walldist[lev]->array(mfi);
70  const auto zcc_arr = z_phys_cc[lev]->const_array(mfi);
71  const auto znd_arr = z_phys_nd[lev]->const_array(mfi);
72  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
73  dist_arr(i, j, k) = zcc_arr(i, j, k) - znd_arr(i, j, 0);
74  });
75  }
76  fill_wall_dist_ghost_cells(*walldist[lev], geom[lev]);
77  return;
78  }
79 #endif
80  }
81  else
82  {
83  Error("No solid boundaries in the computational domain");
84  }
85 
86  if (havewall && solverChoice.wall_dist_type == "terrain_height") {
87  // Height above the local surface projected on the surface normal:
88  // d = (z_cc - z_surf) / sqrt(1 + h_xi^2 + h_eta^2), exact for a
89  // plane, within a few percent of the true distance for hills with
90  // slopes below about 0.3, and free of any linear solve (cf. the
91  // terrain height used by the amr-wind immersed terrain and Kynema).
92  Print() << "Calculating wall distance from the terrain height (normal-projected)" << std::endl;
93  const int klo = geomdata.Domain().smallEnd(2);
94 
95  // The surface nodes z_nd(:,:,klo) live only in the boxes that touch the surface;
96  // when the BoxArray is split in z the boxes above hold nodes from their own k
97  // range, so gather the surface slab onto every box (the same 2D footprint, at klo).
98  BoxList bl_surf = z_phys_nd[lev]->boxArray().boxList();
99  for (auto& b : bl_surf) { b.setRange(2,klo); }
100  BoxArray ba_surf(std::move(bl_surf));
101  IntVect ng_surf = z_phys_nd[lev]->nGrowVect(); ng_surf[2] = 0;
102  MultiFab znd_surf(ba_surf, z_phys_nd[lev]->DistributionMap(), 1, ng_surf);
103  znd_surf.setVal(bogus_large_value);
104  znd_surf.ParallelCopy(*z_phys_nd[lev], 0, 0, 1, ng_surf, ng_surf, geom[lev].periodicity());
105  // Every node the stencil below reads (valid plus the x/y ghosts) must
106  // have been gathered; the slab has no z ghosts, so reduce over ng_surf
107  // rather than a scalar ghost count.
108  Real znd_max = ReduceMax(znd_surf, ng_surf,
109  [=] AMREX_GPU_HOST_DEVICE (Box const& bx, Array4<Real const> const& a) -> Real
110  {
111  Real m = std::numeric_limits<Real>::lowest();
112  amrex::Loop(bx, [&] (int i, int j, int k) { m = amrex::max(m, a(i,j,k)); });
113  return m;
114  });
115  ParallelAllReduce::Max(znd_max, ParallelContext::CommunicatorSub());
117  "poisson_wall_dist: the surface nodes were not gathered onto every box");
118 
119  for (MFIter mfi(*walldist[lev]); mfi.isValid(); ++mfi) {
120  const Box& bx = mfi.validbox();
121  auto dist_arr = walldist[lev]->array(mfi);
122  const auto zcc_arr = z_phys_cc[lev]->const_array(mfi);
123  const auto znd_arr = znd_surf.const_array(mfi);
124  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
125  Real z_surf = fourth * ( znd_arr(i,j,klo) + znd_arr(i+1,j,klo)
126  + znd_arr(i,j+1,klo) + znd_arr(i+1,j+1,klo) );
127  Real h_xi = Compute_h_xi_AtKface (i, j, klo, dxinv, znd_arr);
128  Real h_eta = Compute_h_eta_AtKface(i, j, klo, dxinv, znd_arr);
129  Real dz_loc = zcc_arr(i,j,k) - z_surf;
130  dist_arr(i, j, k) = amrex::max(dz_loc / std::sqrt(one + h_xi*h_xi + h_eta*h_eta),
132  });
133  }
134  fill_wall_dist_ghost_cells(*walldist[lev], geom[lev]);
135  return;
136  }
137 
138  Print() << "Calculating Poisson wall distance for general terrain" << std::endl;
139 
140  // Make sure the solver only sees the levels over which we are solving
141  Vector<Geometry> geom_tmp; geom_tmp.push_back(geom[lev]);
142  Vector<BoxArray> ba_tmp; ba_tmp.push_back(walldist[lev]->boxArray());
143  Vector<DistributionMapping> dm_tmp; dm_tmp.push_back(walldist[lev]->DistributionMap());
144 
145  Vector<MultiFab> rhs;
146  Vector<MultiFab> phi;
147 
148  if (solverChoice.terrain_type == TerrainType::EB) {
149  amrex::Error("Wall dist calc not implemented for EB");
150  } else {
151  rhs.resize(1); rhs[0].define(ba_tmp[0], dm_tmp[0], 1, 0);
152  phi.resize(1); phi[0].define(ba_tmp[0], dm_tmp[0], 1, 1);
153  }
154 
155  rhs[0].setVal(1.0);
156 
157  auto const dom_lo = lbound(geom[lev].Domain());
158  auto const dom_hi = ubound(geom[lev].Domain());
159 
160  // ****************************************************************************
161  // Initialize phi
162  // (It is essential that we do this in order to fill the corners; this is
163  // used if we include blanking.)
164  // ****************************************************************************
165  phi[0].setVal(0.0);
166 
167  // ****************************************************************************
168  // Interior boundaries are marked with phi=0
169  // ****************************************************************************
170 #if 0
171  // Define an overset mask (0 or 1) to set dirichlet nodes on walls
172  // 1 means the node is an unknown. 0 means it's known.
173  iMultiFab mask(ba_tmp[0], dm_tmp[0], 1, 0);
174  Vector<const iMultiFab*> overset_mask = {&mask};
175 
176  mask.setVal(1);
178  Warning("Poisson distance is inaccurate for bodies in open domains that are small compared to the domain size, skipping");
179  return;
180 
181  Gpu::DeviceVector<IntVect> xfacelist, yfacelist, zfacelist;
182 
183  xfacelist.resize(solverChoice.advChoice.zero_xflux.size());
184  yfacelist.resize(solverChoice.advChoice.zero_yflux.size());
185  zfacelist.resize(solverChoice.advChoice.zero_zflux.size());
186 
187  if (xfacelist.size() > 0) {
188  Gpu::copy(amrex::Gpu::hostToDevice,
191  xfacelist.begin());
192  Print() << " masking interior xfaces" << std::endl;
193  }
194  if (yfacelist.size() > 0) {
195  Gpu::copy(amrex::Gpu::hostToDevice,
198  yfacelist.begin());
199  Print() << " masking interior yfaces" << std::endl;
200  }
201  if (zfacelist.size() > 0) {
202  Gpu::copy(amrex::Gpu::hostToDevice,
205  zfacelist.begin());
206  Print() << " masking interior zfaces" << std::endl;
207  }
208 
209  for (MFIter mfi(phi[0]); mfi.isValid(); ++mfi) {
210  const Box& bx = mfi.validbox();
211 
212  auto phi_arr = phi[0].array(mfi);
213  auto mask_arr = mask.array(mfi);
214 
215  if (xfacelist.size() > 0) {
216  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
217  for (int iface=0; iface < xfacelist.size(); ++iface) {
218  if ((i == xfacelist[iface][0]) &&
219  (j == xfacelist[iface][1]) &&
220  (k == xfacelist[iface][2]))
221  {
222  mask_arr(i, j , k ) = 0;
223  mask_arr(i, j , k+1) = 0;
224  mask_arr(i, j+1, k ) = 0;
225  mask_arr(i, j+1, k+1) = 0;
226  }
227  }
228  });
229  }
230 
231  if (yfacelist.size() > 0) {
232  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
233  for (int iface=0; iface < yfacelist.size(); ++iface) {
234  if ((i == yfacelist[iface][0]) &&
235  (j == yfacelist[iface][1]) &&
236  (k == yfacelist[iface][2]))
237  {
238  mask_arr(i , j, k ) = 0;
239  mask_arr(i , j, k+1) = 0;
240  mask_arr(i+1, j, k ) = 0;
241  mask_arr(i+1, j, k+1) = 0;
242  }
243  }
244  });
245  }
246 
247  if (zfacelist.size() > 0) {
248  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
249  for (int iface=0; iface < zfacelist.size(); ++iface) {
250  if ((i == xfacelist[iface][0]) &&
251  (j == xfacelist[iface][1]) &&
252  (k == xfacelist[iface][2]))
253  {
254  mask_arr(i , j , k) = 0;
255  mask_arr(i , j+1, k) = 0;
256  mask_arr(i+1, j , k) = 0;
257  mask_arr(i+1, j+1, k) = 0;
258  }
259  }
260  });
261  }
262  }
263  }
264 #endif
265 
266  // ****************************************************************************
267  // Setup BCs, with solid domain boundaries being dirichlet
268  // ****************************************************************************
269  amrex::Array<amrex::LinOpBCType,AMREX_SPACEDIM> bc3d_lo, bc3d_hi;
270  for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
271  if (geom[lev].isPeriodic(dir)) {
272  bc3d_lo[dir] = LinOpBCType::Periodic;
273  bc3d_hi[dir] = LinOpBCType::Periodic;
274  } else {
275  bc3d_lo[dir] = LinOpBCType::Neumann;
276  bc3d_hi[dir] = LinOpBCType::Neumann;
277  }
278  }
279  if (havewall) {
280  Print() << " Poisson zlo BC is dirichlet" << std::endl;
281  bc3d_lo[2] = LinOpBCType::Dirichlet;
282  }
283  Print() << " bc lo : " << bc3d_lo << std::endl;
284  Print() << " bc hi : " << bc3d_hi << std::endl;
285 
286  if (!solverChoice.advChoice.have_zero_flux_faces && !havewall) {
287  Error("No solid boundaries in the computational domain");
288  }
289 
290  LPInfo info; // defaults
291 
292 /* Nodal solver cannot have hidden dimensions */
293 #if 0
294  // Allow a hidden direction if the domain is one cell wide
295  if (dom_lo.x == dom_hi.x) {
296  info.setHiddenDirection(0);
297  Print() << " domain is 2D in yz" << std::endl;
298  } else if (dom_lo.y == dom_hi.y) {
299  info.setHiddenDirection(1);
300  Print() << " domain is 2D in xz" << std::endl;
301  } else if (dom_lo.z == dom_hi.z) {
302  info.setHiddenDirection(2);
303  Print() << " domain is 2D in xy" << std::endl;
304  }
305 #endif
306 
307 #if 0
308  Vector<EBFArrayBoxFactory const*> factory_vec;
309  factory_vec.push_back(static_cast<FabFactory<FArrayBox> const*>(&EBFactory(lev));
310 #endif
311 
312  // ****************************************************************************
313  // Setup Poisson problem
314  // (A \alpha - B \nabla \cdot \beta \nabla ) \phi = f
315  //
316  // In physical space:
317  // \nabla \cdot \nabla \phi = -1
318  //
319  // In computational space:
320  // grad(phi) = T^T \nabla \phi
321  // and
322  // \nabla \cdot (h_zeta T (T^T \nabla \phi)) = -h_zeta
323  // where T = inv(J), T^T is the transpose of inv(J)
324  //
325  // Posed as -div(beta grad phi) = +h_zeta, i.e. B = +1, the positive
326  // definite form MLABecLaplacian documents (B = -1 with f = -h_zeta is
327  // the same equation and gave the same iterates). Note: on a 3D fitted
328  // mesh with dx != dz this multigrid diverges (residual 18x after the
329  // first cycle, 1e10 by iteration 100) with or without semi-coarsening
330  // and independent of the box layout. Use erf.wall_dist_type =
331  // terrain_height there.
332  // ****************************************************************************
333  constexpr Real constA = zero;
334  constexpr Real constB = one;
335 
336  MLABecLaplacian mlabec(geom_tmp, ba_tmp, dm_tmp, info);
337 
338  mlabec.setScalars(constA, constB);
339  mlabec.setACoeffs(0, zero);
340 #if 1
341  // Set beta coefficients at faces
342  Array<MultiFab, AMREX_SPACEDIM> beta;
343 
344  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
345  BoxArray ba_face = ba_tmp[0];
346  ba_face.surroundingNodes(idim); // Convert to face-centered in direction idim
347  beta[idim].define(ba_face, dm_tmp[0], 1, 0);
348  }
349 
350  auto beta0_arr = beta[0].arrays();
351  auto beta1_arr = beta[1].arrays();
352  auto beta2_arr = beta[2].arrays();
353 
354  // Note: This ignores the off-diagonal components of (h_zeta T T^T), which
355  // is equivalent to assuming that h_xi and h_eta are small.
356 
357  ParallelFor(beta[0], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
358  beta0_arr[b](i, j, k) = Compute_h_zeta_AtIface(i, j, k, dxinv, zphys_arr[b]);;
359  });
360  ParallelFor(beta[1], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
361  beta1_arr[b](i, j, k) = Compute_h_zeta_AtJface(i, j, k, dxinv, zphys_arr[b]);;
362  });
363  ParallelFor(beta[2], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
364  Real inv_h_zeta = one / Compute_h_zeta_AtKface(i, j, k, dxinv, zphys_arr[b]);
365  Real h_xi = Compute_h_xi_AtKface(i, j, k, dxinv, zphys_arr[b]);
366  Real h_eta = Compute_h_eta_AtKface(i, j, k, dxinv, zphys_arr[b]);
367  beta2_arr[b](i, j, k) = inv_h_zeta * (1 + h_xi*h_xi + h_eta*h_eta);
368  });
369 
370  mlabec.setBCoeffs(0, GetArrOfConstPtrs(beta));
371 
372  // Set RHS := +h_zeta (for -div(beta grad phi) = h_zeta)
373  auto rhs_arr = rhs[0].arrays();
374  ParallelFor(rhs[0], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
375  rhs_arr[b](i, j, k) = Compute_h_zeta_AtCellCenter(i, j, k, dxinv, zphys_arr[b]);
376  });
377 #else
378  mlabec.setBCoeffs(0, one);
379 #endif
380 
381  mlabec.setDomainBC(bc3d_lo, bc3d_hi);
382 
383  if (lev > 0) {
384  mlabec.setCoarseFineBC(nullptr, ref_ratio[lev-1], LinOpBCType::Neumann);
385  }
386 
387  // If we have inhomogeneous BCs -- do this after setCoarseFineBC
388  mlabec.setLevelBC(0, nullptr);
389 
390  // ****************************************************************************
391  // Solve Poisson problem with MLMG
392  // ****************************************************************************
393  const Real reltol = solverChoice.poisson_reltol;
394  const Real abstol = solverChoice.poisson_abstol;
395  const int n_corr = solverChoice.ncorr;
396  constexpr int max_iter = 100;
397 
398  MLMG mlmg(mlabec);
399  mlmg.setMaxIter(max_iter);
400  mlmg.setVerbose(mg_verbose);
401  mlmg.setBottomVerbose(0);
402 
403  for (int icorr=0; icorr <= n_corr; ++icorr) {
404  Print()<< "Solving wall distance poisson, icorr=" << icorr << std::endl;
405 
406  mlmg.solve(GetVecOfPtrs(phi),
407  GetVecOfConstPtrs(rhs),
408  reltol, abstol);
409 
410  // ****************************************************************************
411  // Apply BCs: dirichlet (odd) on zlo, neumann (even) / periodic elsewhere
412  // ****************************************************************************
413 
414  // Overwrite with periodic fill outside domain and fine-fine fill inside
415  phi[0].FillBoundary(geom[lev].periodicity());
416 
417  if (!geom[lev].isPeriodic(0)) {
418  for (MFIter mfi(phi[0],true); mfi.isValid(); ++mfi)
419  {
420  Box bx = mfi.tilebox();
421  const Array4<Real>& phi_arr = phi[0].array(mfi);
422  if (bx.smallEnd(0) <= dom_lo.x) {
423  ParallelFor(makeSlab(bx,0,dom_lo.x),
424  [=] AMREX_GPU_DEVICE (int i, int j, int k)
425  {
426  phi_arr(i-1,j,k) = phi_arr(i,j,k); // even BC
427  });
428  } // lo x
429  if (bx.bigEnd(0) >= dom_hi.x) {
430  ParallelFor(makeSlab(bx,0,dom_hi.x),
431  [=] AMREX_GPU_DEVICE (int i, int j, int k)
432  {
433  phi_arr(i+1,j,k) = phi_arr(i,j,k); // even BC
434  });
435  } // hi x
436  } // mfi
437  } // not periodic in x
438 
439  if (!geom[lev].isPeriodic(1)) {
440  for (MFIter mfi(phi[0],true); mfi.isValid(); ++mfi)
441  {
442  Box bx = mfi.tilebox();
443  Box bx2(bx); bx2.grow(0,1);
444  const Array4<Real>& phi_arr = phi[0].array(mfi);
445  if (bx.smallEnd(1) <= dom_lo.y) {
446  ParallelFor(makeSlab(bx2,1,dom_lo.y),
447  [=] AMREX_GPU_DEVICE (int i, int j, int k)
448  {
449  phi_arr(i,j-1,k) = phi_arr(i,j,k); // even BC
450  });
451  } // lo y
452  if (bx.bigEnd(1) >= dom_hi.y) {
453  ParallelFor(makeSlab(bx2,1,dom_hi.y),
454  [=] AMREX_GPU_DEVICE (int i, int j, int k)
455  {
456  phi_arr(i,j+1,k) = phi_arr(i,j,k); // even BC
457  });
458  } // hi y
459 
460  } // mfi
461  } // not periodic in y
462 
463  for (MFIter mfi(phi[0],true); mfi.isValid(); ++mfi)
464  {
465  Box bx = mfi.tilebox();
466  Box bx3(bx); bx3.grow(0,1); bx3.grow(1,1);
467  const Array4<Real>& phi_arr = phi[0].array(mfi);
468  if (bx.smallEnd(2) <= dom_lo.z) {
469  ParallelFor(makeSlab(bx3,2,dom_lo.z),
470  [=] AMREX_GPU_DEVICE (int i, int j, int k)
471  {
472  phi_arr(i,j,k-1) = -phi_arr(i,j,k); // ODD BC
473  });
474  } // lo z
475  if (bx.bigEnd(2) >= dom_hi.z) {
476  ParallelFor(makeSlab(bx3,2,dom_hi.z),
477  [=] AMREX_GPU_DEVICE (int i, int j, int k)
478  {
479  phi_arr(i,j,k+1) = phi_arr(i,j,k); // even BC
480  });
481  } // hi z
482  } // mfi
483 
484  // ****************************************************************************
485  // Compute grad(phi) to get distances
486  // ****************************************************************************
487  auto const& phi_arr = phi[0].const_arrays();
488  //auto rhs_arr = rhs[0].arrays();
489  auto dist_arr = walldist[lev]->arrays();
490 
491  ParallelFor(*walldist[lev], [=] AMREX_GPU_DEVICE(int b, int i, int j, int k) {
492  // Cell-centred gradient of phi in physical space: centred
493  // differences in computational space with the cell-centre
494  // terrain metrics (chain rule for a mesh deformed in z only).
495  // The face fluxes used before sat half a cell below and beside
496  // the centre, which overstated |grad phi| by dz/2 and shortened
497  // every distance by z dz / (2H) on a flat mesh (0.8 % for 64
498  // cells). The ghost cells of phi carry the Dirichlet (odd) value
499  // below the wall and even values elsewhere, so the stencil needs
500  // nothing beyond one ghost cell and the cell's own eight nodes.
501  const auto& p = phi_arr[b];
502  const auto& zp = zphys_arr[b];
503  Real dpdxi = myhalf * (p(i+1,j,k) - p(i-1,j,k)) * dxinv[0];
504  Real dpdeta = myhalf * (p(i,j+1,k) - p(i,j-1,k)) * dxinv[1];
505  Real dpdzeta = myhalf * (p(i,j,k+1) - p(i,j,k-1)) * dxinv[2];
506  Real h_zeta = Compute_h_zeta_AtCellCenter(i, j, k, dxinv, zp);
507  Real h_xi = Compute_h_xi_AtCellCenter (i, j, k, dxinv, zp);
508  Real h_eta = Compute_h_eta_AtCellCenter (i, j, k, dxinv, zp);
509  Real dpdz = dpdzeta / h_zeta;
510  Real dpdx = dpdxi - h_xi * dpdz;
511  Real dpdy = dpdeta - h_eta * dpdz;
512 
513  Real magsqr_dphi = dpdx*dpdx + dpdy*dpdy + dpdz*dpdz;
514  Real mag_dphi = std::sqrt(magsqr_dphi);
515 #if 1
516  // Tucker 2003 Eqn 2
517  dist_arr[b](i, j, k) = -mag_dphi + std::sqrt(magsqr_dphi + 2*phi_arr[b](i, j, k));
518 #else
519  // DEBUG: output phi instead
520  if (i==0 && j==0) AllPrint() << "walldist"<<IntVect(i,j,k) << " = " << dist_arr[b](i,j,k) << std::endl;
521  dist_arr[b](i, j, k) = phi_arr[b](i, j, k);
522 #endif
523  // Update RHS source term to explicitly include cross-terms
524  if (n_corr > 0) {
525  // d/dxi ( h_xi * dphi/dzeta )
526  Real phi_zeta_xlo = fourth * dxinv[2] * ( phi_arr[b](i , j, k+1) - phi_arr[b](i , j, k-1)
527  + phi_arr[b](i-1, j, k+1) - phi_arr[b](i-1, j, k-1) );
528  Real phi_zeta_xhi = fourth * dxinv[2] * ( phi_arr[b](i , j, k+1) - phi_arr[b](i , j, k-1)
529  + phi_arr[b](i+1, j, k+1) - phi_arr[b](i+1, j, k-1) );
530  Real h_xi_xlo = Compute_h_xi_AtIface(i , j, k, dxinv, zphys_arr[b]);
531  Real h_xi_xhi = Compute_h_xi_AtIface(i+1, j, k, dxinv, zphys_arr[b]);
532 
533  // d/deta ( h_eta * dphi/dzeta )
534  Real phi_zeta_ylo = fourth * dxinv[2] * ( phi_arr[b](i, j , k+1) - phi_arr[b](i, j , k-1)
535  + phi_arr[b](i, j-1, k+1) - phi_arr[b](i, j-1, k-1) );
536  Real phi_zeta_yhi = fourth * dxinv[2] * ( phi_arr[b](i, j , k+1) - phi_arr[b](i, j , k-1)
537  + phi_arr[b](i, j+1, k+1) - phi_arr[b](i, j+1, k-1) );
538  Real h_eta_ylo = Compute_h_eta_AtJface(i, j , k, dxinv, zphys_arr[b]);
539  Real h_eta_yhi = Compute_h_eta_AtJface(i, j+1, k, dxinv, zphys_arr[b]);
540 
541  // d/dzeta ( h_xi * dphi/dxi )
542  Real phi_xi_zlo = fourth * dxinv[0] * ( phi_arr[b](i+1, j, k ) - phi_arr[b](i-1, j, k )
543  + phi_arr[b](i+1, j, k-1) - phi_arr[b](i-1, j, k-1) );
544  Real phi_xi_zhi = fourth * dxinv[0] * ( phi_arr[b](i+1, j, k ) - phi_arr[b](i-1, j, k )
545  + phi_arr[b](i+1, j, k+1) - phi_arr[b](i-1, j, k+1) );
546  Real h_xi_zlo = Compute_h_xi_AtKface(i, j, k , dxinv, zphys_arr[b]);
547  Real h_xi_zhi = Compute_h_xi_AtKface(i, j, k+1, dxinv, zphys_arr[b]);
548 
549  // d/dzeta ( h_eta * dphi/deta )
550  Real phi_eta_zlo = fourth * dxinv[1] * ( phi_arr[b](i, j+1, k ) - phi_arr[b](i, j-1, k )
551  + phi_arr[b](i, j+1, k-1) - phi_arr[b](i, j-1, k-1) );
552  Real phi_eta_zhi = fourth * dxinv[1] * ( phi_arr[b](i, j+1, k ) - phi_arr[b](i, j-1, k )
553  + phi_arr[b](i, j+1, k+1) - phi_arr[b](i, j-1, k+1) );
554  Real h_eta_zlo = Compute_h_eta_AtKface(i, j, k , dxinv, zphys_arr[b]);
555  Real h_eta_zhi = Compute_h_eta_AtKface(i, j, k+1, dxinv, zphys_arr[b]);
556 
557  Real detJ = Compute_h_zeta_AtCellCenter(i, j, k, dxinv, zphys_arr[b]);
558 
559  // same sign convention as the predictor: -div(beta grad phi) = detJ - cross terms
560  rhs_arr[b](i, j, k) = detJ
561  - dxinv[0] * ( h_xi_xhi * phi_zeta_xhi - h_xi_xlo * phi_zeta_xlo)
562  - dxinv[1] * ( h_eta_yhi * phi_zeta_yhi - h_eta_ylo * phi_zeta_ylo)
563  - dxinv[2] * ( h_xi_zhi * phi_xi_zhi - h_xi_zlo * phi_xi_zlo
564  + h_eta_zhi * phi_eta_zhi - h_eta_zlo * phi_eta_zlo);
565  }
566  });
567  } // corrector loop
568 
569  // The solve only fills the valid region, so fill the ghost cells here
570  fill_wall_dist_ghost_cells(*walldist[lev], geom[lev]);
571 }
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:292
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:409
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:269
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:433
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:339
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:385
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:456
static int mg_verbose
Definition: ERF.H:1682
amrex::Real poisson_abstol
Absolute tolerance for the Poisson solve.
Definition: ERF_DataStruct.H:2014
int ncorr
Number of projection correction iterations.
Definition: ERF_DataStruct.H:2008
amrex::Real poisson_reltol
Relative tolerance for the Poisson solve.
Definition: ERF_DataStruct.H:2015
std::string wall_dist_type
Definition: ERF_DataStruct.H:2009
Here is the call graph for this function:

◆ post_timestep()

void ERF::post_timestep ( int  nstep,
double  time,
double  dt_lev 
)
371 {
372  BL_PROFILE("ERF::post_timestep()");
373 
374  // nstep is the 0-based index of the step just completed; the balance
375  // reports by the number of completed steps, the plotfiles' numbering.
376  ibseb_report(nstep + 1, time);
377 
378  if (cloud_chamber_budget) {
379  cloud_chamber_budget->report(
380  nstep + 1, time, vars_new[0][Vars::cons], geom[0],
381  solverChoice.moisture_type == MoistureType::SatAdj);
382  }
383 
384 #ifdef ERF_USE_PARTICLES
385  particleData.Redistribute(z_phys_nd);
386 #endif
387 
388  if (solverChoice.coupling_type == CouplingType::TwoWay)
389  {
390  int ncomp = vars_new[0][Vars::cons].nComp();
391  for (int lev = finest_level-1; lev >= 0; lev--)
392  {
393  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
394  // m is the map scale factor at cell centers
395  // Here we pre-divide (rho S) by m^2 before refluxing
396  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
397  const Box& bx = mfi.tilebox();
398  const Array4< Real> cons_arr = vars_new[lev][Vars::cons].array(mfi);
399  const Array4<const Real> mfx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
400  const Array4<const Real> mfy_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
401  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
402  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
403  {
404  cons_arr(i,j,k,n) /= (mfx_arr(i,j,0)*mfy_arr(i,j,0));
405  });
406  } else {
407  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
408  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
409  {
410  cons_arr(i,j,k,n) *= detJ_arr(i,j,k) / (mfx_arr(i,j,0)*mfy_arr(i,j,0));
411  });
412  }
413  } // mfi
414 
415  // This call refluxes all "slow" cell-centered variables
416  // (i.e. not density or (rho theta) or velocities) from the lev/lev+1 interface onto lev
417  getAdvFluxReg(lev+1)->Reflux(vars_new[lev][Vars::cons], 2, 2, ncomp-2);
418 
419  // Here we multiply (rho S) by m^2 after refluxing
420  for (MFIter mfi(vars_new[lev][Vars::cons], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
421  const Box& bx = mfi.tilebox();
422  const Array4< Real> cons_arr = vars_new[lev][Vars::cons].array(mfi);
423  const Array4<const Real> mfx_arr = mapfac[lev][MapFacType::m_x]->const_array(mfi);
424  const Array4<const Real> mfy_arr = mapfac[lev][MapFacType::m_y]->const_array(mfi);
425  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
426  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
427  {
428  cons_arr(i,j,k,n) *= (mfx_arr(i,j,0)*mfy_arr(i,j,0));
429  });
430  } else {
431  const Array4<const Real> detJ_arr = detJ_cc[lev]->const_array(mfi);
432  ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
433  {
434  cons_arr(i,j,k,n) *= (mfx_arr(i,j,0)*mfy_arr(i,j,0)) / detJ_arr(i,j,k);
435  });
436  }
437  } // mfi
438 
439  // We need to do this before anything else because refluxing changes the
440  // values of coarse cells underneath fine grids with the assumption they'll
441  // be over-written by averaging down
442  int src_comp;
443  if (solverChoice.anelastic[lev]) {
444  src_comp = 1;
445  } else {
446  src_comp = 0;
447  }
448  int num_comp = ncomp - src_comp;
449  AverageDownTo(lev,src_comp,num_comp);
450  }
451  }
452 
453  if (is_it_time_for_action(nstep, time, dt_lev0, sum_interval, sum_per)) {
456  sum_energy_quantities(time);
457  }
458 
460  if (is_it_time_for_action(nstep, time, dt_lev0, pert_interval, -one)) {
461  turbPert.debug(time);
462  }
463  }
464 
465  if (profile_int > 0 && (nstep+1) % profile_int == 0) {
466  if (destag_profiles) {
467  // all variables cell-centered
468  write_1D_profiles(time);
469  } else {
470  // some variables staggered
472  }
473  }
474 
476  {
477  if ( rad_datalog_int > 0 &&
478  (((nstep+1) % rad_datalog_int == 0) || (nstep==0)) ) {
479  if (rad[0]->hasDatalog()) {
480  rad[0]->WriteDataLog(time + start_time);
481  }
482  }
483  }
484 
485  if (output_1d_column) {
486 #ifdef ERF_USE_NETCDF
487  if (is_it_time_for_action(nstep, time, dt_lev0, column_interval, column_per))
488  {
489  int lev_column = 0;
490  for (int lev = finest_level; lev >= 0; lev--)
491  {
492  const Box& domain_lev = geom[lev].Domain();
493  Real dx_lev = geom[lev].CellSize(0);
494  Real dy_lev = geom[lev].CellSize(1);
495  // Cell containing (column_loc_x,column_loc_y) -- note that these locations are
496  // measured from ProbLo, which need not be at the origin
497  int i_lev = domain_lev.smallEnd(0) +
498  static_cast<int>(std::floor((column_loc_x - geom[lev].ProbLo(0)) / dx_lev));
499  int j_lev = domain_lev.smallEnd(1) +
500  static_cast<int>(std::floor((column_loc_y - geom[lev].ProbLo(1)) / dy_lev));
501  // Loop runs from finest to coarsest, so stop at the first (finest) level
502  // that contains the column
503  if (grids[lev].contains(IntVect(i_lev,j_lev,domain_lev.smallEnd(2)))) {
504  lev_column = lev; break;
505  }
506  }
507  writeToNCColumnFile(lev_column, column_file_name, column_loc_x, column_loc_y, time);
508  }
509 #else
510  Abort("To output 1D column files ERF must be compiled with NetCDF");
511 #endif
512  }
513 
515  {
518  {
519  bool is_moist = (micro->Get_Qstate_Moist_Size() > 0);
520  m_w2d->write_planes(istep[0], time+start_time, vars_new, is_moist);
521  }
522  }
523 
524  // Write plane/line sampler data
526  const int nlev = static_cast<int>(vars_new.size());
527  Vector<MultiFab*> tau13_lev(nlev, nullptr);
528  Vector<MultiFab*> tau23_lev(nlev, nullptr);
529  Vector<MultiFab*> hfx3_lev(nlev, nullptr);
530  Vector<int> prognostic_tke_available(nlev, 0);
531  for (int lev = 0; lev < nlev; ++lev) {
532  prognostic_tke_available[lev] = solverChoice.turbChoice[lev].use_tke ? 1 : 0;
533  if (lev < static_cast<int>(Tau.size())) {
534  if (Tau[lev].size() > TauType::tau13) {
535  tau13_lev[lev] = Tau[lev][TauType::tau13].get();
536  }
537  if (Tau[lev].size() > TauType::tau23) {
538  tau23_lev[lev] = Tau[lev][TauType::tau23].get();
539  }
540  }
541  if (lev < static_cast<int>(SFS_hfx3_lev.size())) {
542  hfx3_lev[lev] = SFS_hfx3_lev[lev].get();
543  }
544  }
545  line_sampler->get_sample_data(geom, vars_new, tau13_lev, tau23_lev, hfx3_lev,
546  prognostic_tke_available);
547  line_sampler->write_sample_data(t_new, istep, ref_ratio, geom);
548  }
550  plane_sampler->get_sample_data(geom, vars_new);
551  plane_sampler->write_sample_data(t_new, istep, ref_ratio, geom);
552  }
553 
554  // Write station time series
555  if (station_sampler &&
557  sample_stations(static_cast<Real>(time));
558  }
559 
560  // Moving terrain
561  if ( solverChoice.terrain_type == TerrainType::MovingFittedMesh )
562  {
563  for (int lev = finest_level; lev >= 0; lev--)
564  {
565  // Copy z_phs_nd and detJ_cc at end of timestep
566  MultiFab::Copy(*z_phys_nd[lev], *z_phys_nd_new[lev], 0, 0, 1, z_phys_nd[lev]->nGrowVect());
567  MultiFab::Copy( *detJ_cc[lev], *detJ_cc_new[lev], 0, 0, 1, detJ_cc[lev]->nGrowVect());
568  MultiFab::Copy(base_state[lev],base_state_new[lev],0,0,BaseState::num_comps,base_state[lev].nGrowVect());
569 
570  make_zcc(geom[lev],*z_phys_nd[lev],*z_phys_cc[lev]);
571  }
572  }
573 
575  (nstep == 0 or (m_plot3d_int_1 > 0 and (nstep+1)%m_plot3d_int_1 == 0))) {
576 
577  int levc=finest_level;
578 
580 
581  MultiFab& U_new = vars_new[levc][Vars::xvel];
582  MultiFab& V_new = vars_new[levc][Vars::yvel];
583  MultiFab& W_new = vars_new[levc][Vars::zvel];
584 
585  MultiFab mf_cc_vel(grids[levc], dmap[levc], AMREX_SPACEDIM, IntVect(0,0,0));
586  average_face_to_cellcenter(mf_cc_vel,0,{AMREX_D_DECL(&U_new,&V_new,&W_new)},0);
587 
588  HurricaneMaxVelTracker(geom[levc],
589  mf_cc_vel,
590  t_new[0]);
591 
593  geom[levc],
594  vars_new[levc][Vars::cons],
595  t_new[0]);
596 
597  std::string filename_tracker = MakeVTKFilename_TrackerCircle(nstep);
598  std::string filename_xy = MakeVTKFilename_EyeTracker_xy(nstep);
599  std::string filename_latlon = MakeFilename_EyeTracker_latlon(nstep);
600  std::string filename_maxvel = MakeFilename_EyeTracker_maxvel(nstep);
601  std::string filename_minpressure = MakeFilename_EyeTracker_minpressure(nstep);
602 
603  if (ParallelDescriptor::IOProcessor()) {
604  WriteVTKPolyline(filename_tracker, hurricane_tracker_circle);
606  WriteLinePlot(filename_latlon, hurricane_eye_track_latlon);
607  WriteLinePlot(filename_maxvel, hurricane_maxvel_vs_time);
608  WriteLinePlot(filename_minpressure, hurricane_minpressure_vs_time);
609  }
610  }
611 
612 } // post_timestep
void make_zcc(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc)
Definition: ERF_TerrainMetrics.cpp:1048
static double bndry_output_planes_per
Definition: ERF.H:1783
std::string MakeFilename_EyeTracker_maxvel(int nstep)
Definition: ERF_TrackerOutput.cpp:69
static amrex::Real column_loc_y
Definition: ERF.H:1777
static double column_per
Definition: ERF.H:1775
static std::string column_file_name
Definition: ERF.H:1778
AMREX_FORCE_INLINE amrex::YAFluxRegister * getAdvFluxReg(int lev)
Definition: ERF.H:1937
void ibseb_report(int nstep, amrex::Real time)
Definition: ERF_IBSEB.cpp:218
void HurricaneMaxVelTracker(const amrex::Geometry &geom, const amrex::MultiFab &mf_cc_vel, const double &time)
Definition: ERF_HurricaneDiagnostics.cpp:673
void HurricaneEyeTracker(const SolverChoice &solverChoice)
Definition: ERF_HurricaneDiagnostics.cpp:640
static amrex::Real column_loc_x
Definition: ERF.H:1776
std::string MakeVTKFilename_TrackerCircle(int nstep)
Definition: ERF_TrackerOutput.cpp:27
std::string MakeVTKFilename_EyeTracker_xy(int nstep)
Definition: ERF_TrackerOutput.cpp:41
void HurricaneMinPressureTracker(MoistureType moisture_type, const amrex::Geometry &geom, const amrex::MultiFab &mf_cons_var, const double &time)
Definition: ERF_HurricaneDiagnostics.cpp:737
static int bndry_output_planes_interval
Definition: ERF.H:1782
std::string MakeFilename_EyeTracker_minpressure(int nstep)
Definition: ERF_TrackerOutput.cpp:83
void WriteLinePlot(const std::string &filename, amrex::Vector< std::array< amrex::Real, 2 >> &points_xy)
Definition: ERF_Write1DProfiles.cpp:642
static int output_1d_column
Definition: ERF.H:1773
void WriteVTKPolyline(const std::string &filename, amrex::Vector< std::array< amrex::Real, 2 >> &points_xy)
Definition: ERF_TrackerOutput.cpp:97
std::string MakeFilename_EyeTracker_latlon(int nstep)
Definition: ERF_TrackerOutput.cpp:55
static int column_interval
Definition: ERF.H:1774
bool io_hurricane_eye_tracker
Whether hurricane-eye tracking output is enabled.
Definition: ERF_DataStruct.H:2320

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,
double  time,
const amrex::Geometry &  geom 
)
private

◆ print_banner()

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

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

◆ print_usage()

void ERF::print_usage ( MPI_Comm  comm,
std::ostream &  out 
)
static
30 {
31 #ifdef AMREX_USE_MPI
32  int irank = 0;
33  int num_ranks = 1;
34  MPI_Comm_size(comm, &num_ranks);
35  MPI_Comm_rank(comm, &irank);
36 
37  // Only root process does the printing
38  if (irank != 0) return;
39 #else
40  amrex::ignore_unused(comm);
41 #endif
42 
43  out << R"doc(Usage:
44  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,
double  time,
double  l_dt 
)

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

Parameters
levLevel index for the velocity projection
timeTime at which coarse data are registered
l_dtTime step used for coarse data registration
73 {
74  BL_PROFILE("ERF::project_initial_velocity()");
75  // Impose FillBoundary on density since we use it in the conversion of velocity to momentum
76  vars_new[lev][Vars::cons].FillBoundary(geom[lev].periodicity());
77 
78  const MultiFab* c_vfrac = nullptr;
79  if (solverChoice.terrain_type == TerrainType::EB) {
80  c_vfrac = &((get_eb(lev).get_const_factory())->getVolFrac());
81  }
82 
83  VelocityToMomentum(vars_new[lev][Vars::xvel], IntVect{0},
84  vars_new[lev][Vars::yvel], IntVect{0},
85  vars_new[lev][Vars::zvel], IntVect{0},
86  vars_new[lev][Vars::cons],
87  rU_new[lev], rV_new[lev], rW_new[lev],
88  Geom(lev).Domain(), domain_bcs_type, c_vfrac);
89 
90  Vector<MultiFab> tmp_mom;
91 
92  tmp_mom.push_back(MultiFab(vars_new[lev][Vars::cons],make_alias,0,1));
93  tmp_mom.push_back(MultiFab(rU_new[lev],make_alias,0,1));
94  tmp_mom.push_back(MultiFab(rV_new[lev],make_alias,0,1));
95  tmp_mom.push_back(MultiFab(rW_new[lev],make_alias,0,1));
96 
97  // If at lev > 0 we must first fill the velocities at the c/f interface -- this must
98  // be done *after* the projection at lev-1
99  if (lev > 0) {
100  int levc = lev-1;
101 
102  const MultiFab* c_vfrac_crse = nullptr;
103  if (solverChoice.terrain_type == TerrainType::EB) {
104  c_vfrac_crse = &((get_eb(levc).get_const_factory())->getVolFrac());
105  }
106 
107  MultiFab& S_new_crse = vars_new[levc][Vars::cons];
108  MultiFab& U_new_crse = vars_new[levc][Vars::xvel];
109  MultiFab& V_new_crse = vars_new[levc][Vars::yvel];
110  MultiFab& W_new_crse = vars_new[levc][Vars::zvel];
111 
112  VelocityToMomentum(U_new_crse, IntVect{0}, V_new_crse, IntVect{0}, W_new_crse, IntVect{0}, S_new_crse,
113  rU_new[levc], rV_new[levc], rW_new[levc],
114  Geom(levc).Domain(), domain_bcs_type, c_vfrac_crse);
115 
116  rU_new[levc].FillBoundary(geom[levc].periodicity());
117  FPr_u[levc].RegisterCoarseData({&rU_new[levc], &rU_new[levc]}, {time, time+l_dt});
118 
119  rV_new[levc].FillBoundary(geom[levc].periodicity());
120  FPr_v[levc].RegisterCoarseData({&rV_new[levc], &rV_new[levc]}, {time, time+l_dt});
121 
122  rW_new[levc].FillBoundary(geom[levc].periodicity());
123  FPr_w[levc].RegisterCoarseData({&rW_new[levc], &rW_new[levc]}, {time, time+l_dt});
124  }
125 
126  // Use the same time that was registered in the FillPatcher above so that the
127  // FillSet assertion (time >= crse_times[0] && time <= crse_times[1]) is satisfied
128  // when called at non-zero simulation time (restart or mid-run regrid).
129  project_momenta(lev, time, l_dt, tmp_mom);
130 
132  vars_new[lev][Vars::yvel],
133  vars_new[lev][Vars::zvel],
134  vars_new[lev][Vars::cons],
135  rU_new[lev], rV_new[lev], rW_new[lev],
136  Geom(lev).Domain(), domain_bcs_type, c_vfrac);
137  }
void project_momenta(int lev, double l_time, double l_dt, amrex::Vector< amrex::MultiFab > &vars)
Definition: ERF_PoissonSolve.cpp:148
Here is the call graph for this function:

◆ project_momenta()

void ERF::project_momenta ( int  lev,
double  l_time,
double  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

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

◆ project_velocity_tb()

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

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

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

Read the refinement box from the inputs file.

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

824 {
825  Print() << "Restart from native checkpoint " << restart_chkfile << "\n";
826 
827  const auto provenance_result =
829  if (provenance_result.valid() &&
830  provenance_result.record.artifact.artifact_type == erf_provenance::ArtifactType::Checkpoint) {
832  execution_provenance, provenance_result.record, restart_chkfile);
833  } else {
834  // Provenance is auxiliary metadata. A missing or invalid record must not make
835  // a valid physical checkpoint unreadable.
836  if (ParallelDescriptor::IOProcessor() && !provenance_warning_emitted) {
837  provenance_warning_emitted = true;
838  const std::string reason = provenance_result.valid()
839  ? "the provenance record has artifact_type=" +
841  provenance_result.record.artifact.artifact_type)) +
842  ", not checkpoint"
843  : provenance_result.diagnostic;
844  Warning("Cannot recover provenance from native checkpoint '" +
845  restart_chkfile + "': " + reason +
846  ". ERF will continue with incomplete provenance.");
847  }
848  const auto failure_status = provenance_result.valid()
850  : provenance_result.status;
852  execution_provenance, failure_status, restart_chkfile);
853  }
854 
855  // Header
856  std::string File(restart_chkfile + "/Header");
857 
858  VisMF::IO_Buffer io_buffer(VisMF::GetIOBufferSize());
859 
860  Vector<char> fileCharPtr;
861  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
862  std::string fileCharPtrString(fileCharPtr.dataPtr());
863  std::istringstream is(fileCharPtrString, std::istringstream::in);
864 
865  std::string line, word;
866 
867  int chk_ncomp_cons, chk_ncomp;
868 
869  // read in title line
870  std::getline(is, line);
871 
872  // read in finest_level
873  is >> finest_level;
874  GotoNextLine(is);
875 
876  validate_surface_temperature_contract(restart_chkfile, finest_level);
877 
878  // read the number of components
879  // for each variable we store
880 
881  // conservative, cell-centered vars
882  is >> chk_ncomp_cons;
883  GotoNextLine(is);
884 
885  // x-velocity on faces
886  is >> chk_ncomp;
887  GotoNextLine(is);
888  AMREX_ASSERT(chk_ncomp == 1);
889 
890  // y-velocity on faces
891  is >> chk_ncomp;
892  GotoNextLine(is);
893  AMREX_ASSERT(chk_ncomp == 1);
894 
895  // z-velocity on faces
896  is >> chk_ncomp;
897  GotoNextLine(is);
898  AMREX_ASSERT(chk_ncomp == 1);
899 
900  // read in array of istep
901  std::getline(is, line);
902  {
903  std::istringstream lis(line);
904  int i = 0;
905  while (lis >> word) {
906  istep[i++] = std::stoi(word);
907  }
908  }
909 
910  // read in array of dt
911  std::getline(is, line);
912  {
913  std::istringstream lis(line);
914  int i = 0;
915  while (lis >> word) {
916  dt[i++] = std::stod(word);
917  }
918  }
919 
920  // read in array of t_new
921  std::getline(is, line);
922  {
923  std::istringstream lis(line);
924  int i = 0;
925  while (lis >> word) {
926  t_new[i++] = std::stod(word);
927  }
928  }
929 
930  // Read zlevels from its own directory
931  // NOTE: This read should occur before MakeNewLevelFromScratch
932  {
933  std::string ZLevelsFile(restart_chkfile + "/zlevels");
934  if (amrex::FileExists(ZLevelsFile)) {
935  Vector<char> ZLevelsfileCharPtr;
936  ParallelDescriptor::ReadAndBcastFile(ZLevelsFile, ZLevelsfileCharPtr);
937  std::string ZLevelsfileCharPtrString(ZLevelsfileCharPtr.dataPtr());
938  std::istringstream isz(ZLevelsfileCharPtrString, std::istringstream::in);
939 
940  int nlevs_in_chk = 0;
941  isz >> nlevs_in_chk;
942 
943  for (int lev = 0; lev < nlevs_in_chk; ++lev) {
944  int nz_stag = 0;
945  isz >> nz_stag;
946 
947  Vector<Real> zlevels_from_chk(nz_stag);
948  for (int k = 0; k < nz_stag; ++k) {
949  isz >> zlevels_from_chk[k];
950  }
951 
952  // A checkpoint from a run with more levels than we hold: read past it
953  if (lev >= static_cast<int>(zlevels_stag.size())) { continue; }
954 
955  if (static_cast<int>(zlevels_stag[lev].size()) != nz_stag) {
956  Print() << "Checkpoint holds " << nz_stag << " staggered z levels at level "
957  << lev << " but this run expects "
958  << zlevels_stag[lev].size() << std::endl;
959  Abort("Cannot restart with a different number of cells in z");
960  }
961 
962  zlevels_stag[lev] = zlevels_from_chk;
963 
964  // Keep the cell heights consistent with the levels we just read in
966  }
967  } // zlevels file exists
968  }
969 
970  for (int lev = 0; lev <= finest_level; ++lev) {
971  // read in level 'lev' BoxArray from Header
972  BoxArray ba;
973  ba.readFrom(is);
974  GotoNextLine(is);
975 
976  // create a distribution mapping
977  DistributionMapping dm { ba, ParallelDescriptor::NProcs() };
978 
979  MakeNewLevelFromScratch (lev, static_cast<Real>(t_new[lev]), ba, dm);
980  }
981 
982  // read in array of t_avg_cnt
983  //
984  // NOTE: this must come after the loop above, because MakeNewLevelFromScratch ->
985  // init_stuff zeroes the time-averaging state for each level it builds.
986  // An older checkpoint has nothing here, in which case we leave those zeros
987  // in place and the average simply starts over.
989  std::getline(is, line);
990  std::istringstream lis(line);
991  int i = 0;
992  while ((i < t_avg_cnt.size()) && (lis >> word)) {
993  t_avg_cnt[i++] = std::stod(word);
994  }
995  if (i == 0) {
996  amrex::Print() << "NOTE: this checkpoint predates time-averaged velocity being "
997  "checkpointed; the running average of velocity will start over"
998  << std::endl;
999  }
1000  }
1001 
1003  const std::string metadata_name(restart_chkfile + "/IntervalMeansHeader");
1004  if (!amrex::FileExists(metadata_name)) {
1005  for (int lev = 0; lev <= finest_level; ++lev) {
1006  AMREX_ALWAYS_ASSERT(interval_means[lev] != nullptr);
1007  interval_means[lev]->setVal(zero);
1008  t_mean_cnt[lev] = 0.0;
1009  }
1010  // A legacy checkpoint has no interval-mean metadata. Keep the
1011  // safe empty-window fallback, but do not schedule a time reset a
1012  // second time when the checkpoint was written after its threshold.
1014  solverChoice.mean_vars_reset_mode == "time", t_new[0],
1015  static_cast<double>(solverChoice.mean_vars_reset_time));
1016  if (ParallelDescriptor::IOProcessor()) {
1017  amrex::Print() << "WARNING: legacy checkpoint without interval-mean state; "
1018  "the averaging window starts empty.\n";
1019  }
1020  } else {
1021  Vector<char> metadata_chars;
1022  ParallelDescriptor::ReadAndBcastFile(metadata_name, metadata_chars);
1023  std::istringstream metadata(std::string(metadata_chars.dataPtr()),
1024  std::istringstream::in);
1025  erf_interval_means::Metadata parsed_metadata;
1026  std::string metadata_error;
1028  metadata, parsed_metadata, metadata_error)) {
1029  Abort("Invalid interval-mean checkpoint metadata in '" + metadata_name +
1030  "': " + metadata_error);
1031  }
1032  const std::string validation_error = erf_interval_means::validate_metadata(
1033  parsed_metadata, finest_level + 1, 10, grids);
1034  if (!validation_error.empty()) {
1035  Abort(validation_error + " in '" + metadata_name + "'");
1036  }
1037 
1039  for (int lev = 0; lev <= finest_level; ++lev) {
1040  const auto& level_metadata = parsed_metadata.level[lev];
1041 
1042  const std::string mf_name =
1043  MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "IntervalMeans");
1044  if (!amrex::FileExists(mf_name + "_H")) {
1045  Abort("Interval-mean metadata exists but data is missing for level " +
1046  std::to_string(lev));
1047  }
1048  AMREX_ALWAYS_ASSERT(interval_means[lev] != nullptr);
1049  VisMF::Read(*interval_means[lev], mf_name);
1050  t_mean_cnt[lev] = level_metadata.accumulation_count;
1051  }
1052  }
1053  }
1054 
1055  // ncomp is only valid after we MakeNewLevelFromScratch (asks micro how many vars)
1056  // NOTE: Data is written over ncomp, so check that we match the header file
1057  int ncomp_cons = vars_new[0][Vars::cons].nComp();
1058 
1059  // NOTE: QKE was removed so this is for backward compatibility
1060  AMREX_ASSERT((chk_ncomp_cons==ncomp_cons) || ((chk_ncomp_cons-1)==ncomp_cons));
1061  //
1062  // See if we have a written separate file that tells how many components and how many ghost cells
1063  // we have of the base state
1064  //
1065  // If we can't find the file, then set the number of components to the original number = 3
1066  //
1067  int ncomp_base_to_read = 3;
1068  IntVect ng_base = IntVect{1};
1069  {
1070  std::string BaseStateFile(restart_chkfile + "/num_base_state_comps");
1071 
1072  if (amrex::FileExists(BaseStateFile))
1073  {
1074  Vector<char> BaseStatefileCharPtr;
1075  ParallelDescriptor::ReadAndBcastFile(BaseStateFile, BaseStatefileCharPtr);
1076  std::string BaseStatefileCharPtrString(BaseStatefileCharPtr.dataPtr());
1077 
1078  // We set this to the default value of 3 but allow it be larger if th0 and qv0 were written
1079  std::istringstream isb(BaseStatefileCharPtrString, std::istringstream::in);
1080  isb >> ncomp_base_to_read;
1081  isb >> ng_base;
1082  }
1083  }
1084 
1085  // read in the MultiFab data
1086  for (int lev = 0; lev <= finest_level; ++lev)
1087  {
1088  // NOTE: For backward compatibility (chk file has QKE)
1089  if ((chk_ncomp_cons-1)==ncomp_cons) {
1090  MultiFab cons(grids[lev],dmap[lev],chk_ncomp_cons,0);
1091  VisMF::Read(cons, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Cell"));
1092 
1093  // Copy up to RhoKE_comp
1094  MultiFab::Copy(vars_new[lev][Vars::cons],cons,0,0,(RhoKE_comp+1),0);
1095 
1096  // Only if we have a PBL model do we need to copy QKE is src to KE in dst
1097  if ( (solverChoice.turbChoice[lev].pbl_type == PBLType::MYNN25) ||
1098  (solverChoice.turbChoice[lev].pbl_type == PBLType::MYNNEDMF) ) {
1099  MultiFab::Copy(vars_new[lev][Vars::cons],cons,(RhoKE_comp+1),RhoKE_comp,1,0);
1100  vars_new[lev][Vars::cons].mult(myhalf,RhoKE_comp,1,0);
1101  }
1102 
1103  // Copy other components
1104  int ncomp_remainder = ncomp_cons - (RhoKE_comp + 1);
1105  MultiFab::Copy(vars_new[lev][Vars::cons],cons,(RhoKE_comp+2),(RhoKE_comp+1),ncomp_remainder,0);
1106 
1107  vars_new[lev][Vars::cons].setBndry(bogus_large_value);
1108  } else {
1109  MultiFab cons(grids[lev],dmap[lev],ncomp_cons,0);
1110  VisMF::Read(cons, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Cell"));
1111  MultiFab::Copy(vars_new[lev][Vars::cons],cons,0,0,ncomp_cons,0);
1112  vars_new[lev][Vars::cons].setBndry(bogus_large_value);
1113  }
1114 
1115  MultiFab xvel(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
1116  VisMF::Read(xvel, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "XFace"));
1117  MultiFab::Copy(vars_new[lev][Vars::xvel],xvel,0,0,1,0);
1118  vars_new[lev][Vars::xvel].setBndry(bogus_large_value);
1119 
1120  MultiFab yvel(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
1121  VisMF::Read(yvel, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "YFace"));
1122  MultiFab::Copy(vars_new[lev][Vars::yvel],yvel,0,0,1,0);
1123  vars_new[lev][Vars::yvel].setBndry(bogus_large_value);
1124 
1125  MultiFab zvel(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
1126  VisMF::Read(zvel, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "ZFace"));
1127  MultiFab::Copy(vars_new[lev][Vars::zvel],zvel,0,0,1,0);
1128  vars_new[lev][Vars::zvel].setBndry(bogus_large_value);
1129 
1130  if (solverChoice.anelastic[lev] == 1) {
1131  MultiFab ppinc(grids[lev],dmap[lev],1,0);
1132  VisMF::Read(ppinc, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "PP_Inc"));
1133  MultiFab::Copy(pp_inc[lev],ppinc,0,0,1,0);
1134  pp_inc[lev].FillBoundary(geom[lev].periodicity());
1135 
1136  MultiFab gpx(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
1137  VisMF::Read(gpx, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Gpx"));
1138  MultiFab::Copy(gradp[lev][GpVars::gpx],gpx,0,0,1,0);
1139  gradp[lev][GpVars::gpx].FillBoundary(geom[lev].periodicity());
1140 
1141  MultiFab gpy(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
1142  VisMF::Read(gpy, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Gpy"));
1143  MultiFab::Copy(gradp[lev][GpVars::gpy],gpy,0,0,1,0);
1144  gradp[lev][GpVars::gpy].FillBoundary(geom[lev].periodicity());
1145 
1146  MultiFab gpz(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
1147  VisMF::Read(gpz, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Gpz"));
1148  MultiFab::Copy(gradp[lev][GpVars::gpz],gpz,0,0,1,0);
1149  gradp[lev][GpVars::gpz].FillBoundary(geom[lev].periodicity());
1150  }
1151 
1152  // Restore the surface fluxes an ocean model is driven by in flux-passing
1153  // mode. Without this they are zero until the first advance, and the
1154  // coupler packs them before that happens. See the matching write above.
1155  //
1156  // Absent from checkpoints written before this landed, and from any
1157  // written by an uncoupled run, so each is probed rather than assumed.
1158  // Missing means the ocean gets zero forcing for its first step, which is
1159  // silent and wrong, so say so rather than leaving it to be discovered in
1160  // the results.
1162  bool restored_any = false;
1163  bool missing_any = false;
1164 
1165  auto read_if_present = [&] (MultiFab* dst, const char* tag, const IntVect& ixtype)
1166  {
1167  if (dst == nullptr) { return; }
1168  const std::string name =
1169  MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", tag);
1170  if (amrex::FileExists(name + "_H")) {
1171  MultiFab tmp(convert(grids[lev],ixtype),dmap[lev],1,0);
1172  VisMF::Read(tmp, name);
1173  MultiFab::Copy(*dst,tmp,0,0,1,0);
1174  dst->FillBoundary(geom[lev].periodicity());
1175  restored_any = true;
1176  } else {
1177  missing_any = true;
1178  }
1179  };
1180 
1181  read_if_present(Tau.empty() ? nullptr : Tau[lev][TauType::tau13].get(),
1182  "Tau13", IntVect(1,0,1));
1183  read_if_present(Tau.empty() ? nullptr : Tau[lev][TauType::tau23].get(),
1184  "Tau23", IntVect(0,1,1));
1185  read_if_present(SFS_hfx3_lev.empty() ? nullptr : SFS_hfx3_lev[lev].get(),
1186  "SFS_hfx3", IntVect(0,0,1));
1187  read_if_present(SFS_q1fx3_lev.empty() ? nullptr : SFS_q1fx3_lev[lev].get(),
1188  "SFS_q1fx3", IntVect(0,0,1));
1189 
1190  if (missing_any && !restored_any) {
1191  amrex::Print() << "WARNING: restart checkpoint carries no surface fluxes "
1192  << "for level " << lev << ". In flux-passing coupled mode "
1193  << "the ocean will be driven by zero wind stress and zero "
1194  << "heat flux until the first atmosphere advance. Re-run "
1195  << "from a checkpoint written with flux output, or expect "
1196  << "the first ocean step to be unforced." << std::endl;
1197  }
1198  }
1199 
1200  // Restore the running sum of the time-averaged velocity (issue 3654). Older
1201  // checkpoints do not carry it; in that case keep the zeros that init_stuff set
1202  // and drop this level's counter to match, so the average restarts consistently.
1203  if (solverChoice.time_avg_vel) {
1204  AMREX_ALWAYS_ASSERT(vel_t_avg[lev] != nullptr);
1205  const std::string vta_name =
1206  MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "VelTimeAvg");
1207  if (amrex::FileExists(vta_name + "_H")) {
1208  VisMF::Read(*vel_t_avg[lev], vta_name);
1209  } else {
1210  vel_t_avg[lev]->setVal(0.0);
1211  t_avg_cnt[lev] = 0.0;
1212  }
1213  }
1214 
1215  // Note that we read the ghost cells of the base state (unlike above)
1216 
1217  // The original base state only had 3 components and 1 ghost cell -- we read this
1218  // here to be consistent with the old style
1219  MultiFab base(grids[lev],dmap[lev],ncomp_base_to_read,ng_base);
1220  VisMF::Read(base, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "BaseState"));
1221 
1222  MultiFab::Copy(base_state[lev],base,0,0,ncomp_base_to_read,ng_base);
1223 
1224  // Create theta0 from p0, rh0
1225  if (ncomp_base_to_read < 4) {
1226  for (MFIter mfi(base_state[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
1227  {
1228  // We only compute theta_0 on valid cells since we will impose domain BC's after restart
1229  const Box& bx = mfi.tilebox();
1230  Array4<Real> const& fab = base_state[lev].array(mfi);
1231  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1232  {
1234  / fab(i,j,k,BaseState::r0_comp);
1235  });
1236  }
1237  }
1238  // Default theta0 to 0
1239  if (ncomp_base_to_read < 5) {
1240  for (MFIter mfi(base_state[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
1241  {
1242  // We only compute theta_0 on valid cells since we will impose domain BC's after restart
1243  const Box& bx = mfi.tilebox();
1244  Array4<Real> const& fab = base_state[lev].array(mfi);
1245  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1246  {
1247  fab(i,j,k,BaseState::qv0_comp) = zero;
1248  });
1249  }
1250  }
1251  base_state[lev].FillBoundary(geom[lev].periodicity());
1252 
1253  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
1254  // Note that we also read the ghost cells of z_phys_nd
1255  IntVect ng = z_phys_nd[lev]->nGrowVect();
1256  MultiFab z_height(convert(grids[lev],IntVect(1,1,1)),dmap[lev],1,ng);
1257  VisMF::Read(z_height, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Z_Phys_nd"));
1258  MultiFab::Copy(*z_phys_nd[lev],z_height,0,0,1,ng);
1259  update_terrain_arrays(lev);
1260 
1261 #if 0
1262  if ( (solverChoice.init_type != InitType::WRFInput) && (solverChoice.init_type != InitType::Metgrid) ) {
1263  check_mesh_type(lev);
1264  }
1265 #endif
1266  }
1267 
1268  // The min dz the microphysics sizes its sedimentation substeps with, for
1269  // every mesh type as init_zphys does on a fresh start. Set only on fitted
1270  // meshes before, so a restart on a constant-dz mesh left Kessler's (and
1271  // SAM's, Morrison's) dzmin uninitialised and the substep count unbounded:
1272  // the first restarted step of a raining run never finished.
1273  {
1274  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
1275  micro->Set_dzmin(lev, dzmin);
1276  }
1277 
1278  // Read in the moisture model restart variables
1279  std::vector<int> qmoist_indices;
1280  std::vector<std::string> qmoist_names;
1281  micro->Get_Qmoist_Restart_Vars(lev, solverChoice, qmoist_indices, qmoist_names);
1282  int qmoist_nvar = static_cast<int>(qmoist_indices.size());
1283  for (int var = 0; var < qmoist_nvar; var++) {
1284  const int ncomp = 1;
1285  IntVect ng_moist = qmoist[lev][qmoist_indices[var]]->nGrowVect();
1286  MultiFab moist_vars(grids[lev],dmap[lev],ncomp,ng_moist);
1287  VisMF::Read(moist_vars, amrex::MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", qmoist_names[var]));
1288  MultiFab::Copy(*(qmoist[lev][qmoist_indices[var]]),moist_vars,0,0,ncomp,ng_moist);
1289  }
1290 
1291 #if defined(ERF_USE_WINDFARM)
1292  if(solverChoice.windfarm_type == WindFarmType::Fitch or
1293  solverChoice.windfarm_type == WindFarmType::EWP or
1294  solverChoice.windfarm_type == WindFarmType::SimpleAD){
1295  IntVect ng = Nturb[lev].nGrowVect();
1296  MultiFab mf_Nturb(grids[lev],dmap[lev],1,ng);
1297  VisMF::Read(mf_Nturb, amrex::MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "NumTurb"));
1298  MultiFab::Copy(Nturb[lev],mf_Nturb,0,0,1,ng);
1299  }
1300 #endif
1301 
1302  // Read the LSM data
1303  if (solverChoice.lsm_type != LandSurfaceType::None) {
1304  amrex::Print() << "Reading LSM variables" << std::endl;
1305  for (int ivar(0); ivar<lsm_data[lev].size(); ++ivar) {
1306  BoxArray ba = lsm_data[lev][ivar]->boxArray();
1307  DistributionMapping dm = lsm_data[lev][ivar]->DistributionMap();
1308  IntVect ng = lsm_data[lev][ivar]->nGrowVect();
1309  int nvar = lsm_data[lev][ivar]->nComp();
1310  MultiFab lsm_vars(ba,dm,nvar,ng);
1311  VisMF::Read(lsm_vars, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LsmData" + std::to_string(ivar)));
1312  MultiFab::Copy(*(lsm_data[lev][ivar]),lsm_vars,0,0,nvar,ng);
1313  }
1314  for (int iflux(0); iflux<lsm_flux[lev].size(); ++iflux) {
1315  BoxArray ba = lsm_flux[lev][iflux]->boxArray();
1316  DistributionMapping dm = lsm_flux[lev][iflux]->DistributionMap();
1317  IntVect ng = lsm_flux[lev][iflux]->nGrowVect();
1318  int nvar = lsm_flux[lev][iflux]->nComp();
1319  MultiFab lsm_vars(ba,dm,nvar,ng);
1320  VisMF::Read(lsm_vars, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LsmFlux" + std::to_string(iflux)));
1321  MultiFab::Copy(*(lsm_flux[lev][iflux]),lsm_vars,0,0,nvar,ng);
1322  }
1323  }
1324 
1325  // Two-stream radiation: the force-restore surface state
1326  if (solverChoice.rad_type == RadiationType::TwoStream) {
1328  }
1329 
1330  // Read the radiation heating rates
1331  std::string RadFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Qrad_H");
1332  if ((solverChoice.rad_type != RadiationType::None) && amrex::FileExists(RadFileName)) {
1333  amrex::Print() << "Reading radiation heating rates" << std::endl;
1334  int nrad = qheating_rates[lev]->nComp();
1335  MultiFab mf_rad(grids[lev],dmap[lev],nrad,0);
1336  VisMF::Read(mf_rad, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "Qrad"));
1337  MultiFab::Copy(*qheating_rates[lev],mf_rad,0,0,nrad,0);
1338  }
1339 
1340  IntVect ng = mapfac[lev][MapFacType::m_x]->nGrowVect();
1341  MultiFab mf_m(ba2d[lev],dmap[lev],1,ng);
1342 
1343  std::string MapFacMFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_mx_H");
1344  if (amrex::FileExists(MapFacMFileName)) {
1345  VisMF::Read(mf_m, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_mx"));
1346  } else {
1347  VisMF::Read(mf_m, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_m"));
1348  }
1349  MultiFab::Copy(*mapfac[lev][MapFacType::m_x],mf_m,0,0,1,ng);
1350 
1351 #if 0
1353  VisMF::Read(mf_m, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_my"));
1354  MultiFab::Copy(*mapfac[lev][MapFacType::m_y],mf_m,0,0,1,ng);
1355  }
1356 #endif
1357 
1358  ng = mapfac[lev][MapFacType::u_x]->nGrowVect();
1359  MultiFab mf_u(convert(ba2d[lev],IntVect(1,0,0)),dmap[lev],1,ng);
1360 
1361  std::string MapFacUFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_ux_H");
1362  if (amrex::FileExists(MapFacUFileName)) {
1363  VisMF::Read(mf_u, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_ux"));
1364  } else {
1365  VisMF::Read(mf_u, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_u"));
1366  }
1367  MultiFab::Copy(*mapfac[lev][MapFacType::u_x],mf_u,0,0,1,ng);
1368 
1369 #if 0
1371  VisMF::Read(mf_u, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_uy"));
1372  MultiFab::Copy(*mapfac[lev][MapFacType::u_y],mf_u,0,0,1,ng);
1373  }
1374 #endif
1375 
1376  ng = mapfac[lev][MapFacType::v_x]->nGrowVect();
1377  MultiFab mf_v(convert(ba2d[lev],IntVect(0,1,0)),dmap[lev],1,ng);
1378 
1379  std::string MapFacVFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_vx_H");
1380  if (amrex::FileExists(MapFacVFileName)) {
1381  VisMF::Read(mf_v, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_vx"));
1382  } else {
1383  VisMF::Read(mf_v, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_v"));
1384  }
1385  MultiFab::Copy(*mapfac[lev][MapFacType::v_x],mf_v,0,0,1,ng);
1386 
1387 #if 0
1389  VisMF::Read(mf_v, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MapFactor_vy"));
1390  MultiFab::Copy(*mapfac[lev][MapFacType::v_y],mf_v,0,0,1,ng);
1391  }
1392 #endif
1393 
1394 
1395  // NOTE: We read MOST data in ReadCheckpointFileMOST (see below)!
1396 
1397  // See if we wrote out SST data
1398  std::string FirstSSTFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "SST_0_H");
1399  if (amrex::FileExists(FirstSSTFileName))
1400  {
1401  amrex::Print() << "Reading SST data" << std::endl;
1402  int ntimes = 1;
1403  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
1404  MultiFab sst_at_t(ba2d[lev],dmap[lev],1,ng);
1405  sst_lev[lev][0] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ng);
1406  for (int nt(0); nt<ntimes; ++nt) {
1407  VisMF::Read(sst_at_t, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_",
1408  "SST_" + std::to_string(nt)));
1409  MultiFab::Copy(*sst_lev[lev][nt],sst_at_t,0,0,1,ng);
1410  }
1411  }
1412 
1413  // See if we wrote out TSK data
1414  std::string FirstTSKFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "TSK_0_H");
1415  if (amrex::FileExists(FirstTSKFileName))
1416  {
1417  amrex::Print() << "Reading TSK data" << std::endl;
1418  int ntimes = 1;
1419  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
1420  MultiFab tsk_at_t(ba2d[lev],dmap[lev],1,ng);
1421  tsk_lev[lev][0] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ng);
1422  for (int nt(0); nt<ntimes; ++nt) {
1423  VisMF::Read(tsk_at_t, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_",
1424  "TSK_" + std::to_string(nt)));
1425  MultiFab::Copy(*tsk_lev[lev][nt],tsk_at_t,0,0,1,ng);
1426  }
1427  }
1428 
1429  // See if we wrote out LMASK data
1430  std::string LMaskFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LMASK_0_H");
1431  if (amrex::FileExists(LMaskFileName))
1432  {
1433  amrex::Print() << "Reading LMASK data" << std::endl;
1434  int ntimes = 1;
1435  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
1436  MultiFab lmask_at_t(ba2d[lev],dmap[lev],1,ng);
1437  for (int nt(0); nt<ntimes; ++nt) {
1438  VisMF::Read(lmask_at_t, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_",
1439  "LMASK_" + std::to_string(nt)));
1440  for (MFIter mfi(lmask_at_t); mfi.isValid(); ++mfi) {
1441  const Box& bx = mfi.growntilebox();
1442  Array4<int> const& dst_arr = lmask_lev[lev][nt]->array(mfi);
1443  Array4<Real> const& src_arr = lmask_at_t.array(mfi);
1444  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1445  {
1446  dst_arr(i,j,k) = int(src_arr(i,j,k));
1447  });
1448  }
1449  }
1450  } else {
1451  lmask_lev[lev][0]->setVal(solverChoice.is_land[lev]);
1452  lmask_lev[lev][0]->FillBoundary(geom[lev].periodicity());
1453  }
1454 
1455  IntVect ngv = vars_new[lev][Vars::cons].nGrowVect(); ngv[2] = 0;
1456 
1457  // Read lat/lon if it exists
1458  std::string LatFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LAT_H");
1459  if (amrex::FileExists(LatFileName)) {
1460  amrex::Print() << "Reading Lat/Lon variables" << std::endl;
1461  MultiFab lat(ba2d[lev],dmap[lev],1,ngv);
1462  MultiFab lon(ba2d[lev],dmap[lev],1,ngv);
1463  VisMF::Read(lat, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LAT"));
1464  VisMF::Read(lon, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "LON"));
1465  lat_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
1466  lon_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
1467  MultiFab::Copy(*lat_m[lev],lat,0,0,1,ngv);
1468  MultiFab::Copy(*lon_m[lev],lon,0,0,1,ngv);
1469  }
1470 
1471 #ifdef ERF_USE_NETCDF
1472  // Read sinPhi and cosPhi if it exists
1473  std::string VarCorFileName = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "SinPhi_H");
1474  if (amrex::FileExists(VarCorFileName)) {
1475  amrex::Print() << "Reading Coriolis factors" << std::endl;
1476  MultiFab sphi(ba2d[lev],dmap[lev],1,ngv);
1477  MultiFab cphi(ba2d[lev],dmap[lev],1,ngv);
1478  VisMF::Read(sphi, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "SinPhi"));
1479  VisMF::Read(cphi, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "CosPhi"));
1480  sinPhi_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
1481  cosPhi_m[lev] = std::make_unique<MultiFab>(ba2d[lev],dmap[lev],1,ngv);
1482  MultiFab::Copy(*sinPhi_m[lev],sphi,0,0,1,ngv);
1483  MultiFab::Copy(*cosPhi_m[lev],cphi,0,0,1,ngv);
1484  }
1485 
1486  if (solverChoice.use_real_bcs && solverChoice.init_type == InitType::WRFInput) {
1487  if (lev == 0) {
1488  amrex::Print() << "Reading C1H/C2H/RDNW/MUB/PHB variables at level " << lev << std::endl;
1489  MultiFab tmp1d(ba1d[0],dmap[0],1,0);
1490 
1491  VisMF::Read(tmp1d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "C1H"));
1492  MultiFab::Copy(*wrf_C1H,tmp1d,0,0,1,0);
1493 
1494  VisMF::Read(tmp1d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "C2H"));
1495  MultiFab::Copy(*wrf_C2H,tmp1d,0,0,1,0);
1496 
1497  VisMF::Read(tmp1d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "RDNW"));
1498  MultiFab::Copy(*wrf_RDNW,tmp1d,0,0,1,0);
1499 
1500  MultiFab tmp2d(ba2d[0],dmap[0],1,wrf_MUB->nGrowVect());
1501 
1502  VisMF::Read(tmp2d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "MUB"));
1503  MultiFab::Copy(*wrf_MUB,tmp2d,0,0,1,wrf_MUB->nGrowVect());
1504 
1505  MultiFab tmp3d(convert(grids[0],IntVect(0,0,1)),dmap[0],1,wrf_PHB->nGrowVect());
1506 
1507  VisMF::Read(tmp3d, MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", "PHB"));
1508  MultiFab::Copy(*wrf_PHB,tmp3d,0,0,1,wrf_PHB->nGrowVect());
1509  }
1510  }
1511 #endif
1512  } // for lev
1513 
1514  // Restore the LSM scalar step counter (e.g. NoahMP itimestep) so the
1515  // substepping schedule survives restart. lsm.Init() during
1516  // MakeNewLevelFromScratch has already reset itimestep to 0 (NoahmpIOVarInit);
1517  // override it here from the checkpoint. Older checkpoints without this file
1518  // restart with the legacy reset-to-zero behavior.
1519  if (solverChoice.lsm_type != LandSurfaceType::None) {
1520  std::string LsmStepFileName(restart_chkfile + "/lsm_step");
1521  if (amrex::FileExists(LsmStepFileName)) {
1522  Vector<char> LsmStepCharPtr;
1523  ParallelDescriptor::ReadAndBcastFile(LsmStepFileName, LsmStepCharPtr);
1524  std::string LsmStepStr(LsmStepCharPtr.dataPtr());
1525  std::istringstream lsm_is(LsmStepStr, std::istringstream::in);
1526  int step_val = 0;
1527  for (int lev = 0; lev <= finest_level; ++lev) {
1528  if (lsm_is >> step_val) {
1529  lsm.Set_LSM_Step(lev, step_val);
1530  Print() << "Restored LSM step counter at level " << lev
1531  << " to " << step_val << std::endl;
1532  }
1533  }
1534  } else {
1535  Print() << "Warning: legacy checkpoint without lsm_step file; "
1536  << "LSM substep schedule will reset (may break bitwise reproducibility)."
1537  << std::endl;
1538  }
1539 
1540  if (solverChoice.lsm_type == LandSurfaceType::NOAHMP) {
1541  // Restore the full LSM prognostic state (e.g. NoahMP soil/snow/canopy)
1542  // from chk*/noahmp_restart. lsm.Init() during MakeNewLevelFromScratch
1543  // has already cold-initialized this state from wrfinput/tables; this
1544  // overwrites it with the checkpoint, and NoahMP's per-step In-transfer
1545  // pulls it into the physics state on the first Advance (issue #3255).
1546  // Legacy checkpoints without this directory fall back to cold-init.
1547  std::string LsmRestartDir(restart_chkfile + "/noahmp_restart");
1548  for (int lev = 0; lev <= finest_level; ++lev) {
1549  std::string LsmRestartFile = LsmRestartDir + "/Level_" + std::to_string(lev) + ".nc";
1550  if (amrex::FileExists(LsmRestartFile)) {
1551  lsm.Read_Lsm_Restart(lev, LsmRestartDir);
1552  Print() << "Restored full NoahMP prognostic state from "
1553  << LsmRestartFile << " at level " << lev << std::endl;
1554  } else {
1555  Print() << "NoahMP restart file " << LsmRestartFile << " is not present at level " << lev << std::endl;
1556  if (lev == 0) {
1557  Print() << "WARNING: NoahMP will cold-initialize from wrfinput at level " << lev << std::endl;
1558  } else {
1559  Print() << "NOTE: NoahMP will cold-initialize if a wrfinput file is present at level " << lev
1560  << " or it will interpolate from coarse if the file does not exist." << std::endl;
1561  }
1562  }
1563  } // lev
1564  } // NoahMP
1565  } // has LSM
1566 
1567 #ifdef ERF_USE_PARTICLES
1568  restartTracers((ParGDBBase*)GetParGDB(),restart_chkfile);
1569  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
1570  dynamic_cast<LagrangianMicrophysics&>(*micro).restartParticles((ParGDBBase*)GetParGDB(),restart_chkfile);
1571  }
1572 #endif
1573 
1574 #if 0
1575 #ifdef ERF_USE_NETCDF
1576  // Read bdy_data files
1577  if ( ((solverChoice.init_type==InitType::WRFInput) || (solverChoice.init_type==InitType::Metgrid)) &&
1579  {
1580  int ioproc = ParallelDescriptor::IOProcessorNumber(); // I/O rank
1581  int num_time;
1582  int num_var;
1583  Vector<Box> bx_v;
1584  if (ParallelDescriptor::IOProcessor()) {
1585  // Open header file and read from it
1586  std::ifstream bdy_h_file(MultiFabFileFullPrefix(0, restart_chkfile, "Level_", "bdy_H"));
1587  bdy_h_file >> num_time;
1588  bdy_h_file >> num_var;
1589  bdy_h_file >> start_bdy_time;
1590  bdy_h_file >> bdy_time_interval;
1591  bdy_h_file >> real_width;
1592  bx_v.resize(4*num_var);
1593  for (int ivar(0); ivar<num_var; ++ivar) {
1594  bdy_h_file >> bx_v[4*ivar ];
1595  bdy_h_file >> bx_v[4*ivar+1];
1596  bdy_h_file >> bx_v[4*ivar+2];
1597  bdy_h_file >> bx_v[4*ivar+3];
1598  }
1599 
1600  // IO size the FABs
1601  bdy_data_xlo.resize(num_time);
1602  bdy_data_xhi.resize(num_time);
1603  bdy_data_ylo.resize(num_time);
1604  bdy_data_yhi.resize(num_time);
1605  for (int itime(0); itime<num_time; ++itime) {
1606  bdy_data_xlo[itime].resize(num_var);
1607  bdy_data_xhi[itime].resize(num_var);
1608  bdy_data_ylo[itime].resize(num_var);
1609  bdy_data_yhi[itime].resize(num_var);
1610  for (int ivar(0); ivar<num_var; ++ivar) {
1611  bdy_data_xlo[itime][ivar].resize(bx_v[4*ivar ]);
1612  bdy_data_xhi[itime][ivar].resize(bx_v[4*ivar+1]);
1613  bdy_data_ylo[itime][ivar].resize(bx_v[4*ivar+2]);
1614  bdy_data_yhi[itime][ivar].resize(bx_v[4*ivar+3]);
1615  }
1616  }
1617 
1618  // Open data file and read from it
1619  std::ifstream bdy_d_file(MultiFabFileFullPrefix(0, restart_chkfile, "Level_", "bdy_D"));
1620  for (int itime(0); itime<num_time; ++itime) {
1621  for (int ivar(0); ivar<num_var; ++ivar) {
1622  bdy_data_xlo[itime][ivar].readFrom(bdy_d_file);
1623  bdy_data_xhi[itime][ivar].readFrom(bdy_d_file);
1624  bdy_data_ylo[itime][ivar].readFrom(bdy_d_file);
1625  bdy_data_yhi[itime][ivar].readFrom(bdy_d_file);
1626  }
1627  }
1628  } // IO
1629 
1630  // Broadcast the data
1631  ParallelDescriptor::Barrier();
1632  ParallelDescriptor::Bcast(&start_bdy_time,1,ioproc);
1633  ParallelDescriptor::Bcast(&bdy_time_interval,1,ioproc);
1634  ParallelDescriptor::Bcast(&real_width,1,ioproc);
1635  ParallelDescriptor::Bcast(&num_time,1,ioproc);
1636  ParallelDescriptor::Bcast(&num_var,1,ioproc);
1637 
1638  // Everyone size their boxes
1639  bx_v.resize(4*num_var);
1640 
1641  ParallelDescriptor::Bcast(bx_v.dataPtr(),bx_v.size(),ioproc);
1642 
1643  // Everyone but IO size their FABs
1644  if (!ParallelDescriptor::IOProcessor()) {
1645  bdy_data_xlo.resize(num_time);
1646  bdy_data_xhi.resize(num_time);
1647  bdy_data_ylo.resize(num_time);
1648  bdy_data_yhi.resize(num_time);
1649  for (int itime(0); itime<num_time; ++itime) {
1650  bdy_data_xlo[itime].resize(num_var);
1651  bdy_data_xhi[itime].resize(num_var);
1652  bdy_data_ylo[itime].resize(num_var);
1653  bdy_data_yhi[itime].resize(num_var);
1654  for (int ivar(0); ivar<num_var; ++ivar) {
1655  bdy_data_xlo[itime][ivar].resize(bx_v[4*ivar ]);
1656  bdy_data_xhi[itime][ivar].resize(bx_v[4*ivar+1]);
1657  bdy_data_ylo[itime][ivar].resize(bx_v[4*ivar+2]);
1658  bdy_data_yhi[itime][ivar].resize(bx_v[4*ivar+3]);
1659  }
1660  }
1661  }
1662 
1663  for (int itime(0); itime<num_time; ++itime) {
1664  for (int ivar(0); ivar<num_var; ++ivar) {
1665  ParallelDescriptor::Bcast(bdy_data_xlo[itime][ivar].dataPtr(),bdy_data_xlo[itime][ivar].box().numPts(),ioproc);
1666  ParallelDescriptor::Bcast(bdy_data_xhi[itime][ivar].dataPtr(),bdy_data_xhi[itime][ivar].box().numPts(),ioproc);
1667  ParallelDescriptor::Bcast(bdy_data_ylo[itime][ivar].dataPtr(),bdy_data_ylo[itime][ivar].box().numPts(),ioproc);
1668  ParallelDescriptor::Bcast(bdy_data_yhi[itime][ivar].dataPtr(),bdy_data_yhi[itime][ivar].box().numPts(),ioproc);
1669  }
1670  }
1671  } // init_type == WRFInput or Metgrid
1672 #endif
1673 #endif
1674 
1675 #ifdef ERF_USE_NETCDF
1676  // Load boundary data from erfbdy during restart for metgrid or wrfinput.
1677  if (((solverChoice.init_type == InitType::WRFInput) || (solverChoice.init_type == InitType::Metgrid)) &&
1679 
1680  // Check for erfbdy file.
1681  std::string erfbdy_header = erfbdy_file + "/Header";
1682 
1683  // For now we disable this for InitType::WRFInput because it failed the WPS_Test_restart regression test
1684  use_erfbdy = ( (solverChoice.init_type == InitType::Metgrid) && FileSystem::Exists(erfbdy_header) );
1685 
1686  if (solverChoice.init_type == InitType::Metgrid) {
1687  if (!use_erfbdy) {
1688  Abort("Restart with init_type=metgrid requires erfbdy file: " + erfbdy_file);
1689  }
1690  }
1691 
1692  // Load from erfbdy if it exists.
1693  if (use_erfbdy) {
1694  Print() << "Restart: Loading boundary data from erfbdy file: " << erfbdy_file << std::endl;
1695 
1696  int ntimes_erfbdy;
1697  Vector<double> bdy_times;
1698  bdy_time_interval = read_times_from_erfbdy(erfbdy_file,
1699  ntimes_erfbdy, nvars_erfbdy, real_width,
1700  bdy_times, start_bdy_time, final_bdy_time);
1701 
1702  Print() << "Restart: erfbdy file contains " << ntimes_erfbdy << " times" << std::endl;
1703 
1704  bdy_data_xlo.resize(ntimes_erfbdy);
1705  bdy_data_xhi.resize(ntimes_erfbdy);
1706  bdy_data_ylo.resize(ntimes_erfbdy);
1707  bdy_data_yhi.resize(ntimes_erfbdy);
1708 
1709  // Determine which times we need based on current simulation time.
1710  double time_since_start_bdy = t_new[0] + start_time - start_bdy_time;
1711  int n_time_old = std::min(static_cast<int>(time_since_start_bdy / bdy_time_interval), ntimes_erfbdy-1);
1712  int n_time_new = n_time_old + 1;
1713 
1714  // Read the necessary times into memory.
1715  for (int itime = n_time_old; itime <= std::min(n_time_new + 1, ntimes_erfbdy - 1); ++itime) {
1717  bdy_data_xlo, bdy_data_xhi,
1718  bdy_data_ylo, bdy_data_yhi,
1720  Print() << "Restart: Loaded erfbdy time index " << itime << std::endl;
1721  }
1722  }
1723  }
1724 #endif
1725 }
void update_stretched_dz(int lev, Vector< Vector< Real >> const &zlevels_stag, Vector< Vector< Real >> &stretched_dz_h, Vector< Gpu::DeviceVector< Real >> &stretched_dz_d)
Definition: ERF_InitZLevels.cpp:131
double read_times_from_erfbdy(const std::string &bdy_file_name, int &ntimes, int &nvars, int &real_width, Vector< double > &bdy_times, double &start_bdy_time, double &final_bdy_time)
Definition: ERF_ReadFromERFBdy.cpp:13
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)
Definition: ERF_ReadFromERFBdy.cpp:68
std::string erfbdy_file
Definition: ERF.H:1733
int nvars_erfbdy
Definition: ERF.H:1734
bool use_erfbdy
Definition: ERF.H:1732
static void GotoNextLine(std::istream &is)
Definition: ERF_Checkpoint.cpp:138
void MakeNewLevelFromScratch(int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
Definition: ERF_MakeNewLevel.cpp:26
void Read_Lsm_Restart(const int &lev, const std::string &dir)
Definition: ERF_LandSurface.H:208
void Set_LSM_Step(const int &lev, int step)
Definition: ERF_LandSurface.H:199
void read_checkpoint(int lev, const std::string &restart_chkfile)
Read it back when the checkpoint carries it; else keep defaults.
Definition: ERF_TwoStreamRadiation.cpp:279
int legacy_reset_done(bool time_reset_mode, double checkpoint_time, double reset_time)
Definition: ERF_IntervalMeansCheckpoint.H:174
std::string validate_metadata(const Metadata &metadata, int expected_levels, int expected_components, const amrex::Vector< amrex::BoxArray > &expected_boxes)
Definition: ERF_IntervalMeansCheckpoint.H:142
bool parse_metadata(std::istream &input, Metadata &result, std::string &error)
Definition: ERF_IntervalMeansCheckpoint.H:85
int global_reset_done(const Metadata &metadata)
Definition: ERF_IntervalMeansCheckpoint.H:165
ProvenanceReadResult read_job_info_file(const std::string &path)
Definition: ERF_Provenance.cpp:485
ExecutionProvenance make_restart_provenance(const ExecutionProvenance &current_invocation, const ProvenanceRecord &parent_checkpoint, const std::string &checkpoint_path)
Definition: ERF_Provenance.cpp:522
ExecutionProvenance make_incomplete_restart_provenance(const ExecutionProvenance &current_invocation, ProvenanceReadStatus failure_status, const std::string &checkpoint_path)
Definition: ERF_Provenance.cpp:546
const char * artifact_type_token(ArtifactType type) noexcept
Definition: ERF_Provenance.cpp:337
Definition: ERF_IntervalMeansCheckpoint.H:78
amrex::Vector< LevelMetadata > level
Definition: ERF_IntervalMeansCheckpoint.H:81
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.

1772 {
1773  // Count number of surface layer boundaries to determine face prefix
1774  int n_faces = 0;
1775  for (OrientationIter oit; oit; ++oit) {
1776  Orientation ori = oit();
1777  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
1778  n_faces += 1;
1779  }
1780  }
1781 
1782  for (OrientationIter oit; oit; ++oit) {
1783  Orientation ori = oit();
1784  if (m_SurfaceLayer[ori]) {
1785  std::string face = "_" + std::to_string(ori);
1786  // if we only use a single surface layer, don't add the face prefix
1787  if (n_faces == 1 && static_cast<int>(ori) == Orientation::zlo()) {
1788  face = "";
1789  }
1790 
1791  //
1792  // State of the exponential time filter (erf.most.time_average), if this run uses it.
1793  // A checkpoint written before this was persisted has no such file, in which case we
1794  // leave the filter uninitialized and it starts over -- the behavior those files
1795  // already had. Anything unexpected in the file is likewise treated as "start over"
1796  // rather than blending stale state into the average.
1797  //
1798  const bool most_time_avg = m_SurfaceLayer[ori]->mac_avg_is_time_averaged();
1799  Vector<int> most_avg_init;
1800  Vector<Vector<Real>> most_plane_avg;
1801  if (most_time_avg) {
1802  const std::string MostAvgFile(restart_chkfile + "/most_time_average" + face);
1803  if (amrex::FileExists(MostAvgFile)) {
1804  Vector<char> fileCharPtr;
1805  ParallelDescriptor::ReadAndBcastFile(MostAvgFile, fileCharPtr);
1806  std::string fileCharPtrString(fileCharPtr.dataPtr());
1807  std::istringstream ism(fileCharPtrString, std::istringstream::in);
1808 
1809  int navg_chk = 0;
1810  ism >> navg_chk;
1811  if (navg_chk == m_SurfaceLayer[ori]->get_num_mac_avg()) {
1812  for (int lev = 0; lev <= finest_level; ++lev) {
1813  int is_init = 0, n_pavg = 0;
1814  if (!(ism >> is_init >> n_pavg)) { break; }
1815  Vector<Real> pavg(n_pavg);
1816  bool line_ok = true;
1817  for (int iavg(0); iavg < n_pavg; ++iavg) {
1818  if (!(ism >> pavg[iavg])) { line_ok = false; break; }
1819  }
1820  if (!line_ok) { break; }
1821  most_avg_init.push_back(is_init);
1822  most_plane_avg.push_back(pavg);
1823  }
1824  } else {
1825  amrex::Print() << "NOTE: checkpoint holds " << navg_chk << " surface-layer "
1826  "averages but this run expects "
1827  << m_SurfaceLayer[ori]->get_num_mac_avg()
1828  << "; the time filter will start over" << std::endl;
1829  }
1830  } else {
1831  amrex::Print() << "NOTE: this checkpoint does not carry the surface-layer time "
1832  "filter state; the filtered averages will start over"
1833  << std::endl;
1834  }
1835  }
1836 
1837  for (int lev = 0; lev <= finest_level; ++lev)
1838  {
1839  amrex::Print() << "Reading MOST variables for face " << ori << std::endl;
1840 
1841  auto read_most_var = [&] (const std::string& name, MultiFab* dst) {
1842  const std::string mf_name = MultiFabFileFullPrefix(lev, restart_chkfile, "Level_", name);
1843  if (amrex::FileExists(mf_name + "_H")) {
1844  MultiFab m_var;
1845  VisMF::Read(m_var, mf_name);
1846  // The number of ghost cells depends on whether these live on a 2D or a 3D
1847  // BoxArray (see WriteCheckpointFile), and a checkpoint written before the
1848  // fix for issue #3560 may have fewer than the destination holds, so only
1849  // fill as many ghost cells as both sides have
1850  IntVect ng = amrex::min(m_var.nGrowVect(), dst->nGrowVect());
1851  dst->ParallelCopy(m_var, 0, 0, 1, ng, ng, geom[lev].periodicity());
1852  // The file's ghost cells may be stale (never filled before the
1853  // write). As copy sources they reach valid cells through periodic
1854  // images, so copy again from the valid cells alone; only
1855  // domain-boundary ghosts keep the file's values.
1856  dst->ParallelCopy(m_var, 0, 0, 1, IntVect(0), ng, geom[lev].periodicity());
1857  return true;
1858  }
1859  return false;
1860  };
1861 
1862  // U*
1863  read_most_var("Ustar" + face, m_SurfaceLayer[ori]->get_u_star(lev));
1864 
1865  // W*
1866  read_most_var("Wstar" + face, m_SurfaceLayer[ori]->get_w_star(lev));
1867 
1868  // T*
1869  read_most_var("Tstar" + face, m_SurfaceLayer[ori]->get_t_star(lev));
1870 
1871  // Q*
1872  read_most_var("Qstar" + face, m_SurfaceLayer[ori]->get_q_star(lev));
1873 
1874  // Olen
1875  read_most_var("Olen" + face, m_SurfaceLayer[ori]->get_olen(lev));
1876 
1877  // T*
1878  read_most_var("Tsurf" + face, m_SurfaceLayer[ori]->get_t_surf(lev));
1879 
1880  // Qsurf
1881  read_most_var("Qsurf" + face, m_SurfaceLayer[ori]->get_q_surf(lev));
1882 
1883  // PBLH
1884  read_most_var("PBLH" + face, m_SurfaceLayer[ori]->get_pblh(lev));
1885 
1886  // Z0
1887  read_most_var("Z0" + face, m_SurfaceLayer[ori]->get_z0(lev));
1888 
1889  // The exponentially filtered averages. We only mark the filter as initialized
1890  // if every piece of its state came back, so that a partial restore degrades to
1891  // "start the average over" instead of blending in whatever the containers hold.
1892  if (most_time_avg && (lev < static_cast<int>(most_avg_init.size())) && most_avg_init[lev]) {
1893  bool restored_all = m_SurfaceLayer[ori]->set_mac_plane_avg(lev, most_plane_avg[lev]);
1894  for (int iavg(0); iavg < m_SurfaceLayer[ori]->get_num_mac_avg(); ++iavg) {
1895  restored_all = read_most_var("MOSTAvg" + std::to_string(iavg) + face,
1896  m_SurfaceLayer[ori]->get_mac_avg_ptr(lev,iavg)) && restored_all;
1897  }
1898  if (restored_all) {
1899  m_SurfaceLayer[ori]->set_mac_avg_initialized(lev);
1900  } else {
1901  amrex::Print() << "NOTE: the surface-layer time filter state at level " << lev
1902  << " is incomplete in this checkpoint; the filtered averages "
1903  "will start over" << std::endl;
1904  }
1905  }
1906  }
1907  }
1908  }
1909 }

◆ ReadParameters()

void ERF::ReadParameters ( )
private
2604 {
2605  // Record the inputs table before anything below can modify it. Every query
2606  // here and in the classes built afterwards is a queryAdd, which inserts the
2607  // default it was handed when the key is absent, so this is the only point at
2608  // which "the key is in the table" still means "the user wrote it".
2609  user_specified_inputs = ParmParse::getEntries(pp_prefix);
2610 
2611  std::string prob_name = "Undefined";
2612  ParmParse pp_pn("erf");
2613  pp_pn.queryAdd("prob_name", prob_name);
2615  Print() << "Problem name (from inputs file) is: "
2616  << " \"" << prob_name << "\" " << std::endl;
2617 
2618  ParmParse pp(pp_prefix);
2619  ParmParse pp_amr("amr");
2620  {
2621  pp.queryAdd("regrid_level_0_on_restart", regrid_level_0_on_restart);
2622  pp.queryAdd("regrid_int", regrid_int);
2623  pp.queryAdd("check_file", check_file);
2624 
2625  // The regression tests use "amr.restart" and "amr.m_check_int" so we allow
2626  // for those or "erf.restart" / "erf.m_check_int". The amr.* values are
2627  // queried after the erf.* values and therefore take precedence if both
2628  // are specified.
2629  pp.queryAdd("check_int", m_check_int);
2630  pp.queryAdd("check_per", m_check_per);
2631  pp_amr.queryAdd("check_int", m_check_int);
2632  pp_amr.queryAdd("check_per", m_check_per);
2633 
2634  pp.queryAdd("restart", restart_chkfile);
2635  pp_amr.queryAdd("restart", restart_chkfile);
2636 
2637  // Verbosity
2638  pp.queryAdd("v", verbose);
2639  pp.queryAdd("mg_v", mg_verbose);
2640  pp.queryAdd("use_fft", use_fft);
2641 #ifndef ERF_USE_FFT
2642  if (use_fft) {
2643  Abort("You must build with USE_FFT in order to set use_fft = true in your inputs file");
2644  }
2645 #endif
2646 
2647  // Check for NaNs?
2648  pp.queryAdd("check_for_nans", check_for_nans);
2649 
2650  // Frequency of diagnostic output
2651  pp.queryAdd("sum_interval", sum_interval);
2652  pp.queryAdd("sum_period" , sum_per);
2653 
2654  pp.queryAdd("pert_interval", pert_interval);
2655 
2656  // Time step controls
2657  pp.queryAdd("cfl", cfl);
2658  pp.queryAdd("substepping_cfl", sub_cfl);
2659  pp.queryAdd("init_shrink", init_shrink);
2660  pp.queryAdd("change_max", change_max);
2661  pp.queryAdd("dt_max_initial", dt_max_initial);
2662  pp.queryAdd("dt_max", dt_max);
2663 
2664  fixed_dt.resize(max_level+1,-one);
2665  fixed_fast_dt.resize(max_level+1,-one);
2666 
2667  pp.queryAdd("fixed_dt", fixed_dt[0]);
2668  pp.queryAdd("fixed_fast_dt", fixed_fast_dt[0]);
2669 
2670  int nlevs_max = max_level + 1;
2671  istep.resize(nlevs_max, 0);
2672  nsubsteps.resize(nlevs_max, 1);
2673  rad_interp_from_coarse_pending.resize(nlevs_max, 0);
2674  // This is the default
2675  for (int lev = 1; lev <= max_level; ++lev) {
2676  nsubsteps[lev] = MaxRefRatio(lev-1);
2677  }
2678 
2679  if (max_level > 0) {
2680  ParmParse pp_erf("erf");
2681  int count = pp_erf.countval("dt_ref_ratio");
2682  if (count > 0) {
2683  Vector<int> nsub;
2684  nsub.resize(nlevs_max, 0);
2685  if (count == 1) {
2686  pp_erf.queryarr("dt_ref_ratio", nsub, 0, 1);
2687  for (int lev = 1; lev <= max_level; ++lev) {
2688  nsubsteps[lev] = nsub[0];
2689  }
2690  } else {
2691  pp_erf.queryarr("dt_ref_ratio", nsub, 0, max_level);
2692  for (int lev = 1; lev <= max_level; ++lev) {
2693  nsubsteps[lev] = nsub[lev-1];
2694  }
2695  }
2696  }
2697  }
2698 
2699  // Make sure we do this after we have defined nsubsteps above
2700  for (int lev = 1; lev <= max_level; lev++)
2701  {
2702  fixed_dt[lev] = fixed_dt[lev-1] / static_cast<Real>(nsubsteps[lev]);
2703  fixed_fast_dt[lev] = fixed_fast_dt[lev-1] / static_cast<Real>(nsubsteps[lev]);
2704  }
2705 
2706  pp.queryAdd("fixed_mri_dt_ratio", fixed_mri_dt_ratio);
2707 
2708  // We use this to keep track of how many boxes we read in from WRF initialization
2709  num_files_at_level.resize(max_level+1,0);
2710 
2711  // We use this to keep track of how many boxes are specified thru the refinement indicators
2712  num_boxes_at_level.resize(max_level+1,0);
2713  boxes_at_level.resize(max_level+1);
2714 
2715  // We always have exactly one file at level 0
2716  num_boxes_at_level[0] = 1;
2717  boxes_at_level[0].resize(1);
2718  boxes_at_level[0][0] = geom[0].Domain();
2719 
2720 #ifdef ERF_USE_NETCDF
2721  nc_init_file.resize(max_level+1);
2722  have_read_nc_init_file.resize(max_level+1);
2723 
2724  // NetCDF wrfinput initialization files -- possibly multiple files at each of multiple levels
2725  // but we always have exactly one file at level 0
2726  for (int lev = 0; lev <= max_level; lev++) {
2727  const std::string nc_file_names = Concatenate("nc_init_file_",lev,1);
2728  if (pp.contains(nc_file_names.c_str())) {
2729  int num_files = pp.countval(nc_file_names.c_str());
2730  num_files_at_level[lev] = num_files;
2731  nc_init_file[lev].resize(num_files);
2732  have_read_nc_init_file[lev].resize(num_files);
2733  pp.queryarr(nc_file_names.c_str(), nc_init_file[lev],0,num_files);
2734  for (int j = 0; j < num_files; j++) {
2735  Print() << "Reading NC init file names at level " << lev << " and index " << j << " : " << nc_init_file[lev][j] << std::endl;
2736  have_read_nc_init_file[lev][j] = 0;
2737  } // j
2738  } // if pp.contains
2739  } // lev
2740 
2741  // NetCDF wrfbdy lateral boundary file
2742  if (pp.queryAdd("nc_bdy_file", nc_bdy_file)) {
2743  Print() << "Reading NC bdy file name " << nc_bdy_file << std::endl;
2744  }
2745 
2746  // NetCDF wrflow lateral boundary file
2747  if (pp.queryAdd("nc_low_file", nc_low_file)) {
2748  Print() << "Reading NC low file name " << nc_low_file << std::endl;
2749  }
2750 
2751 #endif
2752 
2753  // Options for vertical interpolation of met_em*.nc data.
2754  pp.queryAdd("metgrid_debug_quiescent", metgrid_debug_quiescent);
2755  pp.queryAdd("metgrid_debug_isothermal", metgrid_debug_isothermal);
2756  pp.queryAdd("metgrid_debug_dry", metgrid_debug_dry);
2757  pp.queryAdd("metgrid_debug_psfc", metgrid_debug_psfc);
2758  pp.queryAdd("metgrid_debug_msf", metgrid_debug_msf);
2759  pp.queryAdd("metgrid_interp_theta", metgrid_interp_theta);
2760  pp.queryAdd("metgrid_basic_linear", metgrid_basic_linear);
2761  pp.queryAdd("metgrid_use_below_sfc", metgrid_use_below_sfc);
2762  pp.queryAdd("metgrid_use_sfc", metgrid_use_sfc);
2763  pp.queryAdd("metgrid_retain_sfc", metgrid_retain_sfc);
2764  pp.queryAdd("metgrid_proximity", metgrid_proximity);
2765  pp.queryAdd("metgrid_order", metgrid_order);
2766  pp.queryAdd("metgrid_force_sfc_k", metgrid_force_sfc_k);
2767 
2768  // Options for boundary file.
2769  // NOTE: write_erfbdy is deliberately NOT parsed here. Its default depends on
2770  // solverChoice.init_type, which is not known until init_params() runs
2771  // below, so it is parsed further down once that default has been chosen.
2772  pp.queryAdd("erfbdy_file", erfbdy_file);
2773 
2774  // Set default to FullState for now ... later we will try Perturbation
2775  interpolation_type = StateInterpType::FullState;
2776  pp.query_enum_case_insensitive("interpolation_type" ,interpolation_type);
2777 
2778  PlotFileType plotfile3d_type_temp = PlotFileType::None;
2779  pp.query_enum_case_insensitive("plotfile_type" ,plotfile3d_type_temp);
2780  pp.query_enum_case_insensitive("plotfile_type_1",plotfile3d_type_1);
2781  pp.query_enum_case_insensitive("plotfile_type_2",plotfile3d_type_2);
2782 
2783  PlotFileType plotfile2d_type_temp = PlotFileType::None;
2784  pp.query_enum_case_insensitive("plotfile2d_type" ,plotfile2d_type_temp);
2785  pp.query_enum_case_insensitive("plotfile2d_type_1",plotfile2d_type_1);
2786  pp.query_enum_case_insensitive("plotfile2d_type_2",plotfile2d_type_2);
2787  //
2788  // This option is for backward consistency -- if only plotfile_type is set,
2789  // then it will be used for both 1 and 2 if and only if they are not set
2790  //
2791  // Default is native amrex if no type is specified
2792  //
2793  if (plotfile3d_type_temp == PlotFileType::None) {
2794  if (plotfile3d_type_1 == PlotFileType::None) {
2795  plotfile3d_type_1 = PlotFileType::Amrex;
2796  }
2797  if (plotfile3d_type_2 == PlotFileType::None) {
2798  plotfile3d_type_2 = PlotFileType::Amrex;
2799  }
2800  } else {
2801  if (plotfile3d_type_1 == PlotFileType::None) {
2802  plotfile3d_type_1 = plotfile3d_type_temp;
2803  } else {
2804  Abort("You must set either plotfile_type or plotfile_type_1, not both");
2805  }
2806  if (plotfile3d_type_2 == PlotFileType::None) {
2807  plotfile3d_type_2 = plotfile3d_type_temp;
2808  } else {
2809  Abort("You must set either plotfile_type or plotfile_type_2, not both");
2810  }
2811  }
2812  if (plotfile2d_type_temp == PlotFileType::None) {
2813  if (plotfile2d_type_1 == PlotFileType::None) {
2814  plotfile2d_type_1 = PlotFileType::Amrex;
2815  }
2816  if (plotfile2d_type_2 == PlotFileType::None) {
2817  plotfile2d_type_2 = PlotFileType::Amrex;
2818  }
2819  } else {
2820  if (plotfile2d_type_1 == PlotFileType::None) {
2821  plotfile2d_type_1 = plotfile2d_type_temp;
2822  } else {
2823  Abort("You must set either plotfile2d_type or plotfile2d_type_1, not both");
2824  }
2825  if (plotfile2d_type_2 == PlotFileType::None) {
2826  plotfile2d_type_2 = plotfile2d_type_temp;
2827  } else {
2828  Abort("You must set either plotfile2d_type or plotfile2d_type_2, not both");
2829  }
2830  }
2831 #ifndef ERF_USE_NETCDF
2832  if (plotfile3d_type_1 == PlotFileType::Netcdf ||
2833  plotfile3d_type_2 == PlotFileType::Netcdf ||
2834  plotfile2d_type_1 == PlotFileType::Netcdf ||
2835  plotfile2d_type_2 == PlotFileType::Netcdf) {
2836  Abort("Plotfile type = Netcdf is not allowed without USE_NETCDF = TRUE");
2837  }
2838 #endif
2839 
2840  pp.queryAdd("plot_file_1" , plot3d_file_1);
2841  pp.queryAdd("plot_file_2" , plot3d_file_2);
2842  pp.queryAdd("plot2d_file_1", plot2d_file_1);
2843  pp.queryAdd("plot2d_file_2", plot2d_file_2);
2844 
2845  pp.queryAdd("plot_int_1" , m_plot3d_int_1);
2846  pp.queryAdd("plot_int_2" , m_plot3d_int_2);
2847  pp.queryAdd("plot_per_1" , m_plot3d_per_1);
2848  pp.queryAdd("plot_per_2" , m_plot3d_per_2);
2849 
2850  pp.queryAdd("plot2d_int_1" , m_plot2d_int_1);
2851  pp.queryAdd("plot2d_int_2" , m_plot2d_int_2);
2852  pp.queryAdd("plot2d_per_1", m_plot2d_per_1);
2853  pp.queryAdd("plot2d_per_2", m_plot2d_per_2);
2854 
2855  pp.queryAdd("subvol_file", subvol_file);
2856 
2857  // Should we use format like plt1970-01-01_00:00:Real(00.000000) (if true) or plt00001 (if false)
2858  pp.queryAdd("use_real_time_in_pltname", use_real_time_in_pltname);
2859 
2860  // If use_real_time_in_pltname is false, how many digits should we use for the timestep?
2861  pp.queryAdd("file_name_digits", file_name_digits);
2862 
2863  // Default if subvol_int not specified
2864  m_subvol_int.resize(1); m_subvol_int[0] = -1;
2865  m_subvol_per.resize(1); m_subvol_per[0] = -one;
2866  last_subvol_step.resize(1);
2867  last_subvol_time.resize(1);
2868 
2869  int nsi = pp.countval("subvol_int");
2870  int nsr = pp.countval("subvol_per");
2871 
2872  // We must specify only subvol_int OR subvol_per
2873  AMREX_ALWAYS_ASSERT (!(nsi > 0 && nsr > 0));
2874 
2875  int nsub = -1;
2876  if (nsi > 0 || nsr > 0) {
2877  ParmParse pp_sv("erf.subvol");
2878  int n1 = pp_sv.countval("origin"); int n2 = pp_sv.countval("nxnynz"); int n3 = pp_sv.countval("dxdydz");
2879  if (n1 != n2 || n1 != n3 || n2 != n3) {
2880  Abort("WriteSubvolume: must have same number of entries in origin, nxnynz, and dxdydz.");
2881  }
2882  if ( n1%AMREX_SPACEDIM != 0) {
2883  Abort("WriteSubvolume: origin, nxnynz, and dxdydz must have multiples of AMReX_SPACEDIM");
2884  }
2885  nsub = n1/AMREX_SPACEDIM;
2886  last_subvol_step.resize(nsub);
2887  last_subvol_time.resize(nsub);
2888  m_subvol_int.resize(nsub);
2889  m_subvol_per.resize(nsub);
2890  }
2891 
2892  if (nsi > 0) {
2893  for (int i = 0; i < nsub; i++) m_subvol_per[i] = -one;
2894  if ( nsi == 1) {
2895  m_subvol_int[0] = -1;
2896  pp.get("subvol_int" , m_subvol_int[0]);
2897  // A single value applies to every subdomain
2898  for (int i = 1; i < nsub; i++) m_subvol_int[i] = m_subvol_int[0];
2899  } else if ( nsi == nsub) {
2900  pp.getarr("subvol_int" , m_subvol_int);
2901  } else {
2902  Abort("There must either be a single value of subvol_int or one for every subdomain");
2903  }
2904  }
2905 
2906  if (nsr > 0) {
2907  for (int i = 0; i < nsub; i++) m_subvol_int[i] = -1;
2908  if ( nsr == 1) {
2909  m_subvol_per[0] = -one;
2910  pp.get("subvol_per" , m_subvol_per[0]);
2911  // A single value applies to every subdomain
2912  for (int i = 1; i < nsub; i++) m_subvol_per[i] = m_subvol_per[0];
2913  } else if ( nsr == nsub) {
2914  pp.getarr("subvol_per" , m_subvol_per);
2915  } else {
2916  Abort("There must either be a single value of subvol_per or one for every subdomain");
2917  }
2918  }
2919 
2920  // NOTE: the subvolume variable list is *not* selected here. Choosing which
2921  // "rhoQn" components exist needs the microphysics interface, and that
2922  // is constructed after ReadParameters() returns, so setSubVolVariables
2923  // is called alongside setPlotVariables in the ERF constructor.
2924 
2925  pp.queryAdd("expand_plotvars_to_unif_rr",m_expand_plotvars_to_unif_rr);
2926 
2927  pp.queryAdd("plot_face_vels",m_plot_face_vels);
2928  pp.queryAdd("plot_face_terrain_blanking",m_plot_face_terrain_blanking);
2929 
2930  if ( (m_plot3d_int_1 > 0 && m_plot3d_per_1 > 0) ||
2931  (m_plot3d_int_2 > 0 && m_plot3d_per_2 > zero) ) {
2932  Abort("Must choose only one of plot_int or plot_per");
2933  }
2934  if ( (m_plot2d_int_1 > 0 && m_plot2d_per_1 > 0) ||
2935  (m_plot2d_int_2 > 0 && m_plot2d_per_2 > zero) ) {
2936  Abort("Must choose only one of plot_int or plot_per");
2937  }
2938 
2939  pp.queryAdd("profile_int", profile_int);
2940  pp.queryAdd("destag_profiles", destag_profiles);
2941 
2942  pp.queryAdd("plot_lsm", plot_lsm);
2943 #ifdef ERF_USE_RRTMGP
2944  pp.queryAdd("plot_rad", plot_rad);
2945 #endif
2946  pp.queryAdd("profile_rad_int", rad_datalog_int);
2947 
2948  pp.queryAdd("output_1d_column", output_1d_column);
2949  pp.queryAdd("column_per", column_per);
2950  pp.queryAdd("column_interval", column_interval);
2951  pp.queryAdd("column_loc_x", column_loc_x);
2952  pp.queryAdd("column_loc_y", column_loc_y);
2953  pp.queryAdd("column_file_name", column_file_name);
2954 
2955  // Sampler output frequency
2956  pp.queryAdd("line_sampling_per", line_sampling_per);
2957  pp.queryAdd("line_sampling_interval", line_sampling_interval);
2958  pp.queryAdd("plane_sampling_per", plane_sampling_per);
2959  pp.queryAdd("plane_sampling_interval", plane_sampling_interval);
2960  pp.queryAdd("station_sampling_per", station_sampling_per);
2961  pp.queryAdd("station_sampling_interval", station_sampling_interval);
2962 
2963  // Specify information about outputting planes of data
2964  pp.queryAdd("output_bndry_planes", output_bndry_planes);
2965  pp.queryAdd("bndry_output_planes_interval", bndry_output_planes_interval);
2966  pp.queryAdd("bndry_output_planes_per", bndry_output_planes_per);
2967  pp.queryAdd("bndry_output_start_time", bndry_output_planes_start_time);
2968 
2969  // Specify whether ingest boundary planes of data
2970  pp.queryAdd("input_bndry_planes", input_bndry_planes);
2971 
2972  // Query the total width for wrfbdy interior ghost cells
2973  pp.queryAdd("real_width", real_width);
2974 
2975  // If using real boundaries, do we extrapolate w (or set to 0)
2976  pp.queryAdd("real_extrap_w", real_extrap_w);
2977 
2978  // Query the set and total widths for crse-fine interior ghost cells
2979  pp.queryAdd("cf_width", cf_width);
2980  pp.queryAdd("cf_set_width", cf_set_width);
2981 
2982  // AmrMesh iterate on grids?
2983  bool iterate(true);
2984  pp_amr.queryAdd("iterate_grids",iterate);
2985  if (!iterate) SetIterateToFalse();
2986  }
2987 
2988 #ifdef ERF_USE_PARTICLES
2989  readTracersParams();
2990 #endif
2991 
2992  solverChoice.init_params(max_level,pp_prefix);
2993 
2994  // Implicit acoustic substepping inverts one tridiagonal system per column, so it is
2995  // only well posed if no column is chopped between boxes. That does not require one
2996  // box per column: a level may have several boxes over the same (i,j) -- as it does
2997  // where the refined region is a staircase in z, or covers two separate layers -- as
2998  // long as they do not touch, so that each contiguous run of cells in the column is
2999  // solved by itself. What must not happen is two boxes sharing a face normal to z,
3000  // which would split one column into pieces solved separately, with spurious internal
3001  // boundaries at the seam and an answer that depends on the decomposition.
3002  // amr.no_box_split_dir = 2 -- the ERF default, set in add_par -- is what rules that
3003  // out: the grid generator merges the boxes it makes along z, so no two of them share
3004  // a face normal to z. Refuse to run with any other value while a level substeps
3005  // implicitly.
3006  for (int lev = 0; lev <= max_level; lev++) {
3007  if ( (solverChoice.substepping_type[lev] == SubsteppingType::Implicit) &&
3008  (no_box_split_dir != 2) )
3009  {
3010  Abort("erf.substepping_type = Implicit at level " + std::to_string(lev) +
3011  " requires amr.no_box_split_dir = 2 (the ERF default), so that no two grids "
3012  "share a face normal to z: the implicit substep solve inverts one tridiagonal "
3013  "system per column, and a column chopped at such a seam would instead be "
3014  "solved in pieces, giving an answer that depends on the grid decomposition. "
3015  "(Boxes stacked over the same column are fine as long as they do not touch.) "
3016  "Either remove amr.no_box_split_dir = " +
3017  std::to_string(no_box_split_dir) + " from the inputs file, or set "
3018  "erf.substepping_type = None.");
3019  }
3020  }
3021 
3022  // Set a default value for write_erfbdy following these rules.
3023  // Prioritize write_erfbdy provided by user.
3024  // write_erfbdy must be false for restarts.
3025  // write_erfbdy defaults to true for clean starts of the metgrid or wrfinput pathways.
3026  //
3027  // The context-dependent default is chosen FIRST and the user's value is parsed on
3028  // top of it, so that "did the user set this" never has to be asked. It must not be
3029  // asked: this used to test ParmParse::contains("write_erfbdy") after the key had
3030  // already been parsed with queryAdd up in the metgrid block, and queryAdd inserts
3031  // the default into the global table on a miss. contains() was therefore always
3032  // true, the metgrid/WRFInput default below never fired, no erfbdy was written on a
3033  // clean start, and the following restart aborted in ReadCheckpointFile.
3034  {
3035  ParmParse pp_erfbdy(pp_prefix);
3036  const bool is_restart = !restart_chkfile.empty();
3037  if (!is_restart &&
3038  ((solverChoice.init_type == InitType::Metgrid) || (solverChoice.init_type == InitType::WRFInput))) {
3039  write_erfbdy = true;
3040  }
3041 
3042  // A value given in the inputs file overrides the default chosen above.
3043  pp_erfbdy.queryAdd("write_erfbdy", write_erfbdy);
3044 
3045  if (is_restart && write_erfbdy) {
3046  Abort("Cannot set erf.write_erfbdy = true during restart. erfbdy should only be written during initial runs.");
3047  }
3048  }
3049 
3050  {
3051  ParmParse pp_no_prefix; // Traditionally, max_step and stop_time do not have prefix.
3052  pp_no_prefix.queryAdd("max_step", max_step);
3053  if (max_step < 0) {
3054  max_step = std::numeric_limits<int>::max();
3055  }
3056 
3057  std::string start_datetime, stop_datetime;
3058  if (pp_no_prefix.queryAdd("start_datetime", start_datetime)) {
3059  if (start_datetime.length() == 16) { // YYYY-MM-DD HH:MM
3060  start_datetime += ":00"; // add seconds
3061  }
3062  if (start_datetime.length() != 19) {
3063  Print() << "Got start_datetime = \"" << start_datetime
3064  << "\", format should be " << datetime_format << std::endl;
3065  exit(0);
3066  }
3067  start_time = static_cast<double>(getEpochTime(start_datetime, datetime_format));
3068 
3069 #ifdef ERF_USE_NETCDF
3070  if (solverChoice.init_type == InitType::WRFInput) {
3071  // This is the start time as written in the wrfinput file
3072  double start_time_from_wrfinput = read_start_time_from_wrfinput(0, nc_init_file[0][0]);
3073  if (start_time != start_time_from_wrfinput) {
3074  amrex::Print() << "start_datetime from inputs file = " << start_time <<
3075  " does not match SIMULATION START DATE from wrfinput = " <<
3076  start_time_from_wrfinput << std::endl;
3077  amrex::Abort();
3078  }
3079  } else if (solverChoice.init_type == InitType::Metgrid) {
3080  // This is the start time as written in the metgrid file
3081  double start_time_from_metgrid = read_start_time_from_metgrid(0, nc_init_file[0][0]);
3082  if (start_time != start_time_from_metgrid) {
3083  amrex::Print() << "start_datetime from inputs file = " << start_time <<
3084  " does not match SIMULATION START DATE from metgrid = " <<
3085  start_time_from_metgrid << std::endl;
3086  amrex::Abort();
3087  }
3088  }
3089 #endif
3090  Print() << "Start datetime : " << start_datetime << std::endl;
3091 
3092  use_datetime = true;
3093 
3094  } else {
3095 
3096 #ifdef ERF_USE_NETCDF
3097  if (solverChoice.init_type == InitType::WRFInput) {
3098  // This is the start time as written in the wrfinput file
3099  double start_time_from_wrfinput = read_start_time_from_wrfinput(0, nc_init_file[0][0]);
3100  start_time = start_time_from_wrfinput;
3101 
3102  use_datetime = true;
3103 
3104  if (pp_no_prefix.query("start_time", start_time)) {
3105  amrex::Print() << "start_time should not be set from inputs file; we are reading SIMULATION START DATE from wrfinput" << std::endl;
3106  amrex::Abort();
3107  }
3108  } else if (solverChoice.init_type == InitType::Metgrid) {
3109  // This is the start time as written in the metgrid file
3110  double start_time_from_metgrid = read_start_time_from_metgrid(0, nc_init_file[0][0]);
3111  start_time = start_time_from_metgrid;
3112 
3113  use_datetime = true;
3114 
3115  if (pp_no_prefix.query("start_time", start_time)) {
3116  amrex::Print() << "start_time should not be set from inputs file; we are reading SIMULATION START DATE from metgrid" << std::endl;
3117  amrex::Abort();
3118  }
3119  }
3120 #endif
3121  }
3122 
3123  if (pp_no_prefix.queryAdd("stop_datetime", stop_datetime)) {
3124  if (stop_datetime.length() == 16) { // YYYY-MM-DD HH:MM
3125  stop_datetime += ":00"; // add seconds
3126  }
3127  if (stop_datetime.length() != 19) {
3128  Print() << "Got stop_datetime = \"" << stop_datetime
3129  << "\", format should be " << datetime_format << std::endl;
3130  exit(0);
3131  }
3132 
3133  stop_time = static_cast<double>(getEpochTime(stop_datetime, datetime_format));
3134  Print() << "Stop datetime : " << start_datetime << std::endl;
3135 
3136  } else {
3137 
3138  // stop_time already defaults to numeric_limits<double>::max(), which no user
3139  // would type, so it serves as its own sentinel and we can test the value
3140  // instead of the queryAdd return value (which only reports whether the key
3141  // existed before this call).
3142  pp_no_prefix.queryAdd("stop_time", stop_time);
3143  if (stop_time < std::numeric_limits<double>::max()) {
3144  Print() << "Maximum simulation length based on stop_time: " << stop_time << " s (elapsed) " << std::endl;
3145  amrex::Print() <<" Adding stop time " << stop_time << " to start_time " << start_time << std::endl;
3146  stop_time += start_time;
3147  }
3148  }
3149  }
3150 
3151 #ifndef ERF_USE_NETCDF
3152  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(( (solverChoice.init_type != InitType::WRFInput) &&
3153  (solverChoice.init_type != InitType::Metgrid ) &&
3154  (solverChoice.init_type != InitType::NCFile ) ),
3155  "init_type cannot be 'WRFInput', 'Metgrid' or 'NCFile' if we don't build with netcdf!");
3156 #endif
3157 
3158  // Query the canopy model parameters
3159  {
3160  std::string forestfile;
3161  std::string forest_lai_file, forest_height_file, forest_cd_file;
3162 
3163  bool requested_forest_drag = false;
3164  bool has_forest_drag_switch = pp.query("do_forest_drag", requested_forest_drag);
3165 
3166  bool has_forest_file = pp.query("forest_file", forestfile);
3167  bool has_forest_lai = pp.query("forest_lai_file", forest_lai_file);
3168  bool has_forest_height = pp.query("forest_height_file", forest_height_file);
3169  bool has_forest_cd = pp.query("forest_cd_file", forest_cd_file);
3170 
3171  // Optional constant drag coefficient (alternative to forest_cd_file)
3172  Real forest_cd_const = -1.0;
3173  bool has_forest_cd_const = pp.query("forest_cd", forest_cd_const);
3174 
3175  if (has_forest_cd && has_forest_cd_const) {
3176  Abort("Cannot specify both 'forest_cd_file' and 'forest_cd'. Choose one.");
3177  }
3178 
3179  int forest_tree_type = 1;
3180  Real forest_laimax = 0.8;
3181  pp.query("forest_tree_type", forest_tree_type);
3182  pp.query("forest_laimax", forest_laimax);
3183 
3184  if (has_forest_file && (has_forest_lai || has_forest_height || has_forest_cd || has_forest_cd_const)) {
3185  Abort("Cannot specify both 'forest_file' and gridded forest options. Choose one mode.");
3186  }
3187 
3188  if (has_forest_file) {
3190  for (int lev = 0; lev <= max_level; ++lev) {
3191  m_forest_drag[lev] = std::make_unique<ForestDrag>(forestfile);
3192  }
3193  Print() << "ForestDrag: Using discrete patch mode with file: " << forestfile << "\n";
3194 
3195  } else if (has_forest_lai && has_forest_height && has_forest_cd) {
3196  // Gridded NC mode with Cd from file
3198  for (int lev = 0; lev <= max_level; ++lev) {
3199  m_forest_drag[lev] = std::make_unique<ForestDrag>(
3200  forest_lai_file, forest_height_file, forest_cd_file,
3201  forest_tree_type, forest_laimax);
3202  }
3203  Print() << "ForestDrag: Using gridded NetCDF mode\n"
3204  << " LAI file: " << forest_lai_file << "\n"
3205  << " Height file: " << forest_height_file << "\n"
3206  << " Cd file: " << forest_cd_file << "\n"
3207  << " Tree type: " << forest_tree_type << "\n"
3208  << " LAImax: " << forest_laimax << "\n";
3209 
3210  } else if (has_forest_lai && has_forest_height && has_forest_cd_const) {
3211  // Gridded NC mode with constant Cd
3213  for (int lev = 0; lev <= max_level; ++lev) {
3214  m_forest_drag[lev] = std::make_unique<ForestDrag>(
3215  forest_lai_file, forest_height_file, forest_cd_const,
3216  forest_tree_type, forest_laimax);
3217  }
3218  Print() << "ForestDrag: Using gridded NetCDF mode with constant Cd=" << forest_cd_const << "\n"
3219  << " LAI file: " << forest_lai_file << "\n"
3220  << " Height file: " << forest_height_file << "\n"
3221  << " Tree type: " << forest_tree_type << "\n"
3222  << " LAImax: " << forest_laimax << "\n";
3223 
3224  } else if (has_forest_lai || has_forest_height || has_forest_cd || has_forest_cd_const) {
3225  Abort("Gridded forest mode requires forest_lai_file, forest_height_file, "
3226  "and either forest_cd_file or forest_cd.");
3227  }
3228 
3229  if (has_forest_drag_switch && requested_forest_drag != solverChoice.do_forest_drag) {
3230  Abort("'do_forest_drag' must be true when a complete forest configuration is "
3231  "provided, and false when no forest configuration is provided.");
3232  }
3233 
3236  Abort("Forest biophysics was requested (forest_biophysics=" +
3237  std::string(solverChoice.forest_biophysics ? "true" : "false") +
3238  ", forest_biophysics_heat=" +
3239  std::string(solverChoice.forest_biophysics_heat ? "true" : "false") +
3240  ") but no complete forest configuration was supplied. Provide "
3241  "forest_lai_file, forest_height_file, and forest_cd or forest_cd_file.");
3242  }
3243 
3244  }
3245 
3246  // If init from WRFInput or Metgrid make sure a valid file name is present at level zero
3247  // We allow for the possibility that finer levels may use native refinement rather than reading from a file
3248  if ((solverChoice.init_type == InitType::WRFInput) ||
3249  (solverChoice.init_type == InitType::Metgrid) ||
3250  (solverChoice.init_type == InitType::NCFile) ) {
3251  int num_files = static_cast<int>(nc_init_file[0].size());
3252  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(num_files>0, "A file name must be present at level 0 for init type WRFInput, Metgrid or NCFile.");
3253  for (int j = 0; j < num_files; j++) {
3254  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.");
3255  } //j
3256  } // InitType
3257 
3258  // What type of land surface model to use
3259  // NOTE: Must be checked after init_params
3260  if (solverChoice.lsm_type == LandSurfaceType::SLM) {
3261  lsm.SetModel<SLM>();
3262  Print() << "SLM land surface model!\n";
3263 #ifdef ERF_USE_NOAHMP
3264  } else if (solverChoice.lsm_type == LandSurfaceType::NOAHMP) {
3265  lsm.SetModel<NOAHMP>();
3266  Print() << "Noah-MP land surface model!\n";
3267 #endif
3268  } else if (solverChoice.lsm_type == LandSurfaceType::None) {
3269  lsm.SetModel<NullSurf>();
3270  Print() << "Null land surface model!\n";
3271  } else {
3272  Abort("Dont know this LandSurfaceType!") ;
3273  }
3274 
3275  if (verbose > 0) {
3276  solverChoice.display(max_level,pp_prefix);
3277  }
3278 
3280 }
AMREX_GPU_HOST AMREX_FORCE_INLINE std::time_t getEpochTime(const std::string &dateTime, const std::string &dateTimeFormat)
Definition: ERF_EpochTime.H:49
bool metgrid_basic_linear
Definition: ERF.H:1722
bool metgrid_debug_msf
Definition: ERF.H:1720
std::string plot2d_file_2
Definition: ERF.H:1532
std::string plot3d_file_1
Definition: ERF.H:1529
bool plot_rad
Definition: ERF.H:1328
bool m_plot_face_vels
Definition: ERF.H:1547
std::string plot3d_file_2
Definition: ERF.H:1530
bool write_erfbdy
Definition: ERF.H:1731
bool metgrid_retain_sfc
Definition: ERF.H:1725
int file_name_digits
Definition: ERF.H:1698
bool metgrid_use_sfc
Definition: ERF.H:1724
bool metgrid_debug_quiescent
Definition: ERF.H:1716
bool metgrid_interp_theta
Definition: ERF.H:1721
bool regrid_level_0_on_restart
Definition: ERF.H:1526
int metgrid_force_sfc_k
Definition: ERF.H:1728
bool real_extrap_w
Definition: ERF.H:1710
bool metgrid_use_below_sfc
Definition: ERF.H:1723
std::string subvol_file
Definition: ERF.H:1533
amrex::Real metgrid_proximity
Definition: ERF.H:1726
std::string plot2d_file_1
Definition: ERF.H:1531
bool metgrid_debug_dry
Definition: ERF.H:1718
bool metgrid_debug_isothermal
Definition: ERF.H:1717
bool use_real_time_in_pltname
Definition: ERF.H:1699
bool metgrid_debug_psfc
Definition: ERF.H:1719
void ParameterSanityChecks()
Definition: ERF.cpp:3284
bool m_expand_plotvars_to_unif_rr
Definition: ERF.H:1534
std::string check_file
Definition: ERF.H:1557
int metgrid_order
Definition: ERF.H:1727
bool m_plot_face_terrain_blanking
Definition: ERF.H:1548
bool plot_lsm
Definition: ERF.H:1550
void SetModel()
Definition: ERF_LandSurface.H:32
Definition: ERF_NOAHMP.H:57
Definition: ERF_NullSurf.H:10
Definition: ERF_SLM.H:25
void init_params()
Definition: ERF_IBSEBParams.H:149
void display(int max_level, std::string pp_prefix)
Print the configured solver, physics, and transport options.
Definition: ERF_DataStruct.H:1717
void init_params(int max_level, std::string pp_prefix)
Read solver-wide algorithmic options from the input parameter database.
Definition: ERF_DataStruct.H:669

Referenced by ERF_shared().

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

◆ ReadStormTrackerRestart()

void ERF::ReadStormTrackerRestart ( )

Read hurricane tracking history from restart files.

476 {
477  hurricane_eye_track_xy.clear();
479  hurricane_maxvel_vs_time.clear();
481 
482  const fs::path base_dir("Output_StormTracker");
483 
484  // Nothing to do for a fresh run.
485  if (!fs::exists(base_dir)) {
486  return;
487  }
488  //
489  // Return the alphabetically last file in a directory.
490  // Since the filenames are zero-padded, this is also the newest output.
491  //
492  auto last_file = [](const fs::path& dir) -> fs::path
493  {
494  std::vector<fs::path> files;
495 
496  for (const auto& entry : fs::directory_iterator(dir)) {
497  if (entry.is_regular_file()) {
498  files.push_back(entry.path());
499  }
500  }
501 
502  if (files.empty()) {
503  return fs::path{};
504  }
505 
506  std::sort(files.begin(), files.end());
507 
508  return files.back();
509  };
510 
511  //==========================================================
512  // Read lat/lon file
513  //==========================================================
514 
515  {
516  fs::path file = last_file(base_dir / "latlon");
517 
518  if (!file.empty())
519  {
520  std::ifstream ifs(file);
521 
522  if (!ifs.is_open()) {
523  Abort("Could not open " + file.string());
524  }
525 
526  std::string line;
527 
528  // Skip the header line.
529  std::getline(ifs, line);
530 
531  Real lat, lon;
532 
533  while (ifs >> lat >> lon)
534  {
535  hurricane_eye_track_latlon.push_back({lat, lon});
536  }
537  }
538  }
539 
540  //==========================================================
541  // Read maxvel tracker file
542  //==========================================================
543 
544  {
545  fs::path file = last_file(base_dir / "maxvel");
546 
547  if (!file.empty())
548  {
549  std::ifstream ifs(file);
550 
551  if (!ifs.is_open()) {
552  Abort("Could not open " + file.string());
553  }
554 
555  std::string line;
556 
557  // Skip the header line.
558  std::getline(ifs, line);
559 
560  amrex::Real val1, val2;
561 
562  while (ifs >> val1 >> val2)
563  {
564  hurricane_maxvel_vs_time.push_back({val1, val2});
565  }
566  }
567  }
568 
569  //==========================================================
570  // Read minpressure tracker file
571  //==========================================================
572 
573  {
574  fs::path file = last_file(base_dir / "minpressure");
575 
576  if (!file.empty())
577  {
578  std::ifstream ifs(file);
579 
580  if (!ifs.is_open()) {
581  Abort("Could not open " + file.string());
582  }
583 
584  std::string line;
585 
586  // Skip the header line.
587  std::getline(ifs, line);
588 
589  amrex::Real val1, val2;
590 
591  while (ifs >> val1 >> val2)
592  {
593  hurricane_minpressure_vs_time.push_back({val1, val2});
594  }
595  }
596  }
597 
598  //==========================================================
599  // Read XY VTK file
600  //==========================================================
601 
602  {
603  fs::path file = last_file(base_dir / "xy");
604 
605  if (!file.empty())
606  {
607  std::ifstream ifs(file);
608  std::string line;
609 
610  // Skip the first four header lines.
611  for (int i = 0; i < 4; ++i) {
612  std::getline(ifs, line);
613  }
614 
615 
616  std::getline(ifs, line);
617  std::istringstream iss(line);
618  std::string keyword, datatype;
619  int npoints;
620 
621  iss >> keyword >> npoints >> datatype;
622  hurricane_eye_track_xy.reserve(npoints);
623 
624  for (int i = 0; i < npoints; ++i)
625  {
626  Real x, y, z;
627  ifs >> x >> y >> z;
628  hurricane_eye_track_xy.push_back({x, y});
629  }
630  }
631  }
632 }

◆ ReadVelsOnlyFromCheckpointFile()

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

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

1732 {
1733  Print() << "Reading vels only from native checkpoint " << chkfile_for_vels << " at level " << lev_to_fill << "\n";
1734 
1735  // Header
1736  std::string File(chkfile_for_vels + "/Header");
1737 
1738  VisMF::IO_Buffer io_buffer(VisMF::GetIOBufferSize());
1739 
1740  Vector<char> fileCharPtr;
1741  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
1742  std::string fileCharPtrString(fileCharPtr.dataPtr());
1743  std::istringstream is(fileCharPtrString, std::istringstream::in);
1744 
1745  AMREX_ALWAYS_ASSERT(lev_to_fill >= 0 && lev_to_fill <= finest_level);
1746 
1747  int lev = lev_to_fill;
1748 
1749  MultiFab xvel(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
1750  VisMF::Read(xvel, MultiFabFileFullPrefix(lev, chkfile_for_vels, "Level_", "XFace"));
1751  MultiFab::Copy(vars_new[lev][Vars::xvel],xvel,0,0,1,0);
1752  vars_new[lev][Vars::xvel].setBndry(bogus_large_value);
1753 
1754  MultiFab yvel(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
1755  VisMF::Read(yvel, MultiFabFileFullPrefix(lev, chkfile_for_vels, "Level_", "YFace"));
1756  MultiFab::Copy(vars_new[lev][Vars::yvel],yvel,0,0,1,0);
1757  vars_new[lev][Vars::yvel].setBndry(bogus_large_value);
1758 
1759  MultiFab zvel(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
1760  VisMF::Read(zvel, MultiFabFileFullPrefix(lev, chkfile_for_vels, "Level_", "ZFace"));
1761  MultiFab::Copy(vars_new[lev][Vars::zvel],zvel,0,0,1,0);
1762  vars_new[lev][Vars::zvel].setBndry(bogus_large_value);
1763 }
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

503 {
504  if (max_level > 0)
505  {
506  ParmParse pp(pp_prefix);
507  Vector<std::string> refinement_indicators;
508  pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
509 
510  for (int i=0; i<refinement_indicators.size(); ++i)
511  {
512  std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
513 
514  ParmParse ppr(ref_prefix);
515  RealBox real_box;
516  int lev_for_box;
517 
518  read_box_for_refinement(ref_prefix, lev_for_box, real_box);
519 
520  AMRErrorTagInfo info;
521 
522  if (real_box.ok()) {
523  info.SetRealBox(real_box);
524  }
525 
526  if (ppr.countval("start_time") > 0) {
527  double ref_min_time; ppr.get("start_time",ref_min_time);
528  info.SetMinTime(static_cast<Real>(ref_min_time));
529  }
530 
531  if (ppr.countval("end_time") > 0) {
532  double ref_max_time; ppr.get("end_time",ref_max_time);
533  info.SetMaxTime(static_cast<Real>(ref_max_time));
534  }
535 
536  if (ppr.countval("max_level") > 0) {
537  int ref_max_level; ppr.get("max_level",ref_max_level);
538  info.SetMaxLevel(ref_max_level);
539  }
540 
541  // Read field_name once and validate moisture-field requests against
542  // the active moisture model so an unsupported field aborts at setup
543  // rather than after the first regrid.
544  std::string field;
545  if (ppr.countval("field_name") > 0) {
546  ppr.get("field_name", field);
547  auto is_moist_field = [](const std::string& f) {
548  return f == "qv" || f == "qc" || f == "qi" ||
549  f == "qr" || f == "qs" || f == "qg" || f == "qt";
550  };
551  if (is_moist_field(field)) {
552  const auto& mi = solverChoice.moisture_indices;
553  int comp = -1;
554  if (field == "qv") { comp = mi.qv; }
555  else if (field == "qc") { comp = mi.qc; }
556  else if (field == "qi") { comp = mi.qi; }
557  else if (field == "qr") { comp = mi.qr; }
558  else if (field == "qs") { comp = mi.qs; }
559  else if (field == "qg") { comp = mi.qg; }
560  else if (field == "qt") {
561  comp = (mi.qc >= 0 || mi.qi >= 0 || mi.qr >= 0 ||
562  mi.qs >= 0 || mi.qg >= 0) ? 0 : -1;
563  }
564  if (comp < 0) {
565  amrex::Abort("Refinement field_name '" + field +
566  "' is not available for the configured moisture model");
567  }
568  }
569  }
570 
571  // Every push_back into ref_tags must be matched by a push_back into
572  // ref_tag_indicator_names so that ErrorEst can recover the ParmParse
573  // prefix of the indicator that created each tag. Indicators such as
574  // "storm_tracker" create no ref_tag at all, so the index into ref_tags
575  // is not in general the index into refinement_indicators.
576  if (ppr.countval("value_greater")) {
577  int num_val = ppr.countval("value_greater");
578  Vector<Real> value(num_val);
579  ppr.getarr("value_greater",value,0,num_val);
580  ref_tags.push_back(AMRErrorTag(value,AMRErrorTag::GREATER,field,info));
581  ref_tag_indicator_names.push_back(refinement_indicators[i]);
582  }
583  else if (ppr.countval("value_less"))
584  {
585  int num_val = ppr.countval("value_less");
586  Vector<Real> value(num_val);
587  ppr.getarr("value_less",value,0,num_val);
588  ref_tags.push_back(AMRErrorTag(value,AMRErrorTag::LESS,field,info));
589  ref_tag_indicator_names.push_back(refinement_indicators[i]);
590  }
591  else if (ppr.countval("adjacent_difference_greater"))
592  {
593  int num_val = ppr.countval("adjacent_difference_greater");
594  Vector<Real> value(num_val);
595  ppr.getarr("adjacent_difference_greater",value,0,num_val);
596  ref_tags.push_back(AMRErrorTag(value,AMRErrorTag::GRAD,field,info));
597  ref_tag_indicator_names.push_back(refinement_indicators[i]);
598  }
599  else if (real_box.ok())
600  {
601  ref_tags.push_back(AMRErrorTag(info));
602  ref_tag_indicator_names.push_back(refinement_indicators[i]);
603  }
604  else if ( (lev_for_box > 0) && (refinement_indicators[i] != "storm_tracker") )
605  {
606  Abort(std::string("Unrecognized refinement indicator for " + refinement_indicators[i]).c_str());
607  }
609  } // loop over criteria
610  } // if max_level > 0
611 }
void read_box_for_refinement(std::string &ref_prefix, int &lev_for_box, amrex::RealBox &real_box)
Definition: ERF_RefineBox.cpp:13

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,
amrex::Real  time,
std::unique_ptr< amrex::MultiFab > &  temp_zphys_nd 
)
1091 {
1092  if (solverChoice.init_type != InitType::WRFInput && solverChoice.init_type != InitType::Metgrid)
1093  {
1094  if (lev == 0) {
1095  temp_zphys_nd->ParallelCopy(*z_phys_nd[lev], 0, 0, 1, z_phys_nd[lev]->nGrowVect(), z_phys_nd[lev]->nGrowVect());
1096  } else {
1097  //
1098  // First interpolate from coarser level
1099  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
1100  // have been pre-filled - this includes ghost cells both inside and outside
1101  // the domain
1102  //
1103  InterpFromCoarseLevel(*temp_zphys_nd, z_phys_nd[lev]->nGrowVect(),
1104  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
1105  *z_phys_nd[lev-1], 0, 0, 1,
1106  geom[lev-1], geom[lev],
1107  refRatio(lev-1), &node_bilinear_interp,
1109 
1110  //
1111  // On a fine level with STF or Sullivan TF we have to rebuild the mesh the
1112  // same way init_zphys() did, since make_terrain_fitted_coords() cannot
1113  // apply the transformation to a level that does not span the full column
1114  //
1115  ParmParse pp("erf");
1116  int terrain_smoothing = 0;
1117  pp.query("terrain_smoothing", terrain_smoothing);
1118 
1119  FineTerrain fine_terrain = (terrain_smoothing != 0) ? which_fine_terrain()
1121 
1122  MultiFab z_phys_interp;
1123  if (fine_terrain == FineTerrain::Transform) {
1124  z_phys_interp.define(temp_zphys_nd->boxArray(), temp_zphys_nd->DistributionMap(),
1125  1, temp_zphys_nd->nGrowVect());
1126  MultiFab::Copy(z_phys_interp, *temp_zphys_nd, 0, 0, 1, 0); // valid region only
1127  fill_terrain_surface(lev, *temp_zphys_nd, time);
1128  }
1129 
1130  // This recomputes the fine values using the bottom terrain at the fine resolution,
1131  // and also fills values of z_phys_nd outside the domain
1132  make_terrain_fitted_coords(lev,geom[lev],*temp_zphys_nd,zlevels_stag[lev],phys_bc_type,
1133  fine_terrain,
1134  (fine_terrain == FineTerrain::Transform) ? &z_phys_interp : nullptr);
1135 
1136  } // lev > 0
1137 
1138  std::swap(temp_zphys_nd, z_phys_nd[lev]);
1139 
1140  } else {
1141  if (lev == 0) {
1142  temp_zphys_nd->ParallelCopy(*z_phys_nd[lev], 0, 0, 1, z_phys_nd[lev]->nGrowVect(), z_phys_nd[lev]->nGrowVect());
1143  } else {
1144  //
1145  // First interpolate from coarser level
1146  // NOTE: this interpolater assumes that ALL ghost cells of the coarse MultiFab
1147  // have been pre-filled - this includes ghost cells both inside and outside
1148  // the domain
1149  //
1150  InterpFromCoarseLevel(*temp_zphys_nd, z_phys_nd[lev]->nGrowVect(),
1151  z_phys_nd[lev]->nGrowVect(), // DO fill ghost cells outside the domain
1152  *z_phys_nd[lev-1], 0, 0, 1,
1153  geom[lev-1], geom[lev],
1154  refRatio(lev-1), &node_bilinear_interp,
1156 
1157  //
1158  // If this level has its own wrfinput / met_em file then its terrain was built
1159  // from that file's PH + PHB, and the coarse interpolation above is only the
1160  // right answer in the parts of the NEW grids that the OLD grids did not cover.
1161  // ParallelCopy writes only where source and destination overlap, so this keeps
1162  // the file-derived terrain wherever we already had it and leaves the
1163  // coarse-interpolated values everywhere else.
1164  //
1165  // Without this, every regrid would quietly demote a nested level back onto
1166  // coarse-interpolated heights -- which is the same thing the guard in
1167  // init_zphys above exists to prevent at initialization.
1168  //
1169  if (!nc_init_file[lev].empty()) {
1170  temp_zphys_nd->ParallelCopy(*z_phys_nd[lev], 0, 0, 1,
1171  z_phys_nd[lev]->nGrowVect(), z_phys_nd[lev]->nGrowVect());
1172  }
1173 
1174  } // lev > 0
1175 
1176  std::swap(temp_zphys_nd, z_phys_nd[lev]);
1177  }
1178 
1179  if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
1180  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
1181  //
1182  // This assumes we have already remade the EBGeometry
1183  //
1184  // Read the small_volfrac threshold from eb2 namespace
1185  Real small_volfrac = 0.005;
1186  ParmParse pp_eb2("eb2");
1187  pp_eb2.queryAdd("small_volfrac", small_volfrac);
1188 
1189  terrain_blanking[lev]->setVal(one);
1190  MultiFab::Subtract(*terrain_blanking[lev], EBFactory(lev).getVolFrac(), 0, 0, 1, z_phys_nd[lev]->nGrowVect());
1191 
1192  // Clip small terrain_blanking values (almost fluid cells) using same threshold as eb2.small_volfrac
1193  if (small_volfrac > zero) {
1194  for (MFIter mfi(*terrain_blanking[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1195  const Box& bx = mfi.tilebox();
1196  auto const& tblank = terrain_blanking[lev]->array(mfi);
1197  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1198  if (tblank(i,j,k) < small_volfrac) {
1199  tblank(i,j,k) = zero;
1200  }
1201  });
1202  }
1203  }
1204 
1205 #if USE_FC_FACTORY
1206  // Face-centered terrain blanking from face-centered EB volume fractions
1207  const int ng_sub = std::min(ComputeGhostCells(solverChoice) + 2, EBFactory(lev).getVolFrac().nGrow());
1208 
1209  terrain_blanking_xface[lev]->setVal(one);
1210  terrain_blanking_yface[lev]->setVal(one);
1211  terrain_blanking_zface[lev]->setVal(one);
1212  // Check if face factories are available before using them
1213  auto const* u_factory = eb[lev]->get_u_const_factory();
1214  auto const* v_factory = eb[lev]->get_v_const_factory();
1215  auto const* w_factory = eb[lev]->get_w_const_factory();
1216 
1217  if (u_factory && v_factory && w_factory) {
1218  MultiFab::Subtract(*terrain_blanking_xface[lev],
1219  u_factory->getVolFrac(), 0, 0, 1, ng_sub);
1220  MultiFab::Subtract(*terrain_blanking_yface[lev],
1221  v_factory->getVolFrac(), 0, 0, 1, ng_sub);
1222  MultiFab::Subtract(*terrain_blanking_zface[lev],
1223  w_factory->getVolFrac(), 0, 0, 1, ng_sub);
1224 
1225  // Clip small terrain_blanking values on faces (almost fluid cells) using same threshold
1226  if (small_volfrac > zero) {
1227  for (MFIter mfi(*terrain_blanking_xface[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1228  const Box& xbx = mfi.tilebox();
1229  auto const& tblank_x = terrain_blanking_xface[lev]->array(mfi);
1230  ParallelFor(xbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1231  if (tblank_x(i,j,k) < small_volfrac) {
1232  tblank_x(i,j,k) = zero;
1233  }
1234  });
1235  }
1236  for (MFIter mfi(*terrain_blanking_yface[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1237  const Box& ybx = mfi.tilebox();
1238  auto const& tblank_y = terrain_blanking_yface[lev]->array(mfi);
1239  ParallelFor(ybx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1240  if (tblank_y(i,j,k) < small_volfrac) {
1241  tblank_y(i,j,k) = zero;
1242  }
1243  });
1244  }
1245  for (MFIter mfi(*terrain_blanking_zface[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
1246  const Box& zbx = mfi.tilebox();
1247  auto const& tblank_z = terrain_blanking_zface[lev]->array(mfi);
1248  ParallelFor(zbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
1249  if (tblank_z(i,j,k) < small_volfrac) {
1250  tblank_z(i,j,k) = zero;
1251  }
1252  });
1253  }
1254  }
1255  }
1256 #endif
1257  }
1258 
1259  // Compute the min dz and pass to the micro model
1260  Real dzmin = get_dzmin_terrain(*z_phys_nd[lev]);
1261  micro->Set_dzmin(lev, dzmin);
1262 }
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
701 {
702  //
703  // Note that "time" here is elapsed time
704  //
705  if (verbose) {
706  amrex::Print() <<" REMAKING WITH NEW BA AT LEVEL " << lev << " " << ba << std::endl;
707  }
708 
709  AMREX_ALWAYS_ASSERT(solverChoice.terrain_type != TerrainType::MovingFittedMesh);
710 
711  BoxArray ba_old(vars_new[lev][Vars::cons].boxArray());
712  DistributionMapping dm_old(vars_new[lev][Vars::cons].DistributionMap());
713 
714  if (verbose) {
715  amrex::Print() <<" OLD BA AT LEVEL " << lev << " " << ba_old << std::endl;
716  }
717 
718  //
719  // Re-define subdomain at this level within the domain such that
720  // 1) all boxes in a given subdomain are "connected"
721  // 2) no boxes in a subdomain touch any boxes in any other subdomain
722  //
723  subdomains[lev].clear();
724  make_subdomains(ba.simplified_list(), subdomains[lev]);
725 
726  // ********************************************************************************************
727  // Define grids[lev]/dmap[lev] to be the passed-in ba/dm *now*, not on the way out.
728  //
729  // AmrCore::regrid does not call SetBoxArray/SetDistributionMap for this level until this
730  // function returns, so for the whole body below grids[lev]/dmap[lev] otherwise still
731  // describe the PRE-regrid decomposition. Anything that reads them instead of the ba/dm
732  // passed in silently builds itself on the old grids. This is idempotent with the calls
733  // AmrCore makes after we return, and is the only place it happens for lev == 0.
734  // ********************************************************************************************
735  SetBoxArray(lev, ba);
736  SetDistributionMap(lev, dm);
737 
738  int ncomp_cons = vars_new[lev][Vars::cons].nComp();
739  IntVect ngrow_state = vars_new[lev][Vars::cons].nGrowVect();
740 
741  int ngrow_vels = ComputeGhostCells(solverChoice);
742 
743  Vector<MultiFab> temp_lev_new(Vars::NumTypes);
744  Vector<MultiFab> temp_lev_old(Vars::NumTypes);
745  MultiFab temp_base_state;
746 
747  std::unique_ptr<MultiFab> temp_zphys_nd;
748 
749  //
750  // init_stuff below rebuilds mapfac[lev] on the new grids and resets it to 1, and nothing
751  // downstream of a regrid reads the map factors back out of a file, so hold on to the
752  // pre-regrid arrays here and restore them afterwards. Without this a level-0 regrid
753  // (which restart() does whenever the checkpoint has fewer grids than ranks) silently
754  // threw away the MAPFAC_M/U/V read from wrfinput_d01.
755  //
756  Vector<std::unique_ptr<MultiFab>> old_mapfac(mapfac[lev].size());
757  for (int i = 0; i < mapfac[lev].size(); i++) {
758  old_mapfac[i] = std::move(mapfac[lev][i]);
759  }
760 
761  //
762  // The surface pressure and the land mask / land type / soil type are in exactly the
763  // same position: init_stuff rebuilds them on the new grids and leaves a placeholder
764  // (an uninitialized PSFC, a uniform land mask and type 0), only init_from_wrfinput
765  // ever puts the real PSFC / LANDMASK / IVGTYP / ISLTYP there, and a regrid does not
766  // re-read the file. So retain them here too.
767  //
768  // Retaining the land mask matters off the WRF pathways as well: init_zphys marks the
769  // cells under a building with a land mask of 2, and a regrid goes through
770  // remake_zphys instead, which does not redo that marking.
771  //
772  auto retain = [] (Vector<Vector<std::unique_ptr<iMultiFab>>>& v, int l)
773  { return (v[l].empty()) ? nullptr : std::move(v[l][0]); };
774 
775  std::unique_ptr<MultiFab> old_psfc = std::move(mf_PSFC[lev]);
776  std::unique_ptr<iMultiFab> old_lmask = retain( lmask_lev,lev);
777  std::unique_ptr<iMultiFab> old_land_type = retain(land_type_lev,lev);
778  std::unique_ptr<iMultiFab> old_soil_type = retain(soil_type_lev,lev);
779 
780  //********************************************************************************************
781  // This allocates all kinds of things, including but not limited to: solution arrays,
782  // terrain arrays and metrics, and base state.
783  // *******************************************************************************************
784  init_stuff(lev, ba, dm, temp_lev_new, temp_lev_old, temp_base_state, temp_zphys_nd);
785 
786  //
787  // Restore the map factors onto the new grids. At lev > 0 we interpolate from the parent
788  // first so that cells the new grids added -- which the pre-regrid arrays never covered --
789  // are filled as well, then copy the retained values on top wherever we still have them.
790  // (For a level with no init file of its own the Interp2DArrays call further down repeats
791  // that interpolation, which is what defines its map factors in the first place.)
792  //
793  if (lev > 0) { interp_mapfac_from_coarse(lev); }
794  for (int i = 0; i < mapfac[lev].size(); i++) {
795  if (!mapfac[lev][i] || !old_mapfac[i]) { continue; }
796  IntVect ngv = mapfac[lev][i]->nGrowVect(); ngv[2] = 0;
797  mapfac[lev][i]->ParallelCopy(*old_mapfac[i], 0, 0, 1, ngv, ngv, geom[lev].periodicity());
798  mapfac[lev][i]->FillBoundary(geom[lev].periodicity());
799  }
800 
801  //
802  // Restore the surface pressure and the land arrays the same way. Both interpolation
803  // helpers return without doing anything off the wrfinput pathway, since there these
804  // arrays hold what was asked for at this level rather than anything derived from the
805  // parent; the copy below is what carries them across the regrid in that case.
806  //
807  if (lev > 0) {
810  }
811  if (mf_PSFC[lev] && old_psfc) {
812  IntVect ngv = mf_PSFC[lev]->nGrowVect(); ngv[2] = 0;
813  mf_PSFC[lev]->ParallelCopy(*old_psfc, 0, 0, 1, ngv, ngv, geom[lev].periodicity());
814  mf_PSFC[lev]->FillBoundary(geom[lev].periodicity());
815  }
816  {
817  auto entry = [] (Vector<Vector<std::unique_ptr<iMultiFab>>>& v, int l) -> iMultiFab*
818  { return (v[l].empty()) ? nullptr : v[l][0].get(); };
819 
820  Vector<iMultiFab*> new_land = {entry( lmask_lev,lev),
821  entry(land_type_lev,lev),
822  entry(soil_type_lev,lev)};
823  Vector<iMultiFab*> old_land = {old_lmask.get(), old_land_type.get(), old_soil_type.get()};
824  for (int i = 0; i < new_land.size(); i++) {
825  if (!new_land[i] || !old_land[i]) { continue; }
826  IntVect ngv = new_land[i]->nGrowVect(); ngv[2] = 0;
827  new_land[i]->ParallelCopy(*old_land[i], 0, 0, 1, ngv, ngv, geom[lev].periodicity());
828  new_land[i]->FillBoundary(geom[lev].periodicity());
829  }
830  }
831 
832  // ********************************************************************************************
833  // Build the data structures for terrain-related quantities
834  // ********************************************************************************************
835  if ( solverChoice.terrain_type == TerrainType::EB ||
836  solverChoice.terrain_type == TerrainType::ImmersedForcing ||
837  solverChoice.buildings_type == BuildingsType::ImmersedForcing)
838  {
839  const amrex::EB2::IndexSpace& ebis = amrex::EB2::IndexSpace::top();
840  const EB2::Level& eb_level = ebis.getLevel(geom[lev]);
841  if (solverChoice.terrain_type == TerrainType::EB) {
842  eb[lev]->make_all_factories(lev, geom[lev], ba, dm, eb_level);
843  } else if (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
844  solverChoice.buildings_type == BuildingsType::ImmersedForcing) {
845 #if USE_FC_FACTORY
846  eb[lev]->make_all_factories(lev, geom[lev], ba, dm, eb_level);
847 #else
848  eb[lev]->make_cc_factory(lev, geom[lev], ba, dm, eb_level);
849 #endif
850  }
851  }
852  remake_zphys(lev, time, temp_zphys_nd);
854 
855  // ********************************************************************************************
856  // Make sure that detJ is the average of the data on a finer level if there is one
857  // Note that this shouldn't be necessary because the fine grid is created by interpolation
858  // from the coarse ... but just in case ...
859  // ********************************************************************************************
860  if ( (SolverChoice::mesh_type != MeshType::ConstantDz) && (solverChoice.coupling_type == CouplingType::TwoWay) ) {
861  // NOTE: z_phys_cc is deliberately NOT averaged down. Every level's base state is
862  // built to be in discrete hydrostatic balance against that level's own
863  // cell-centered heights, so replacing the coarse heights with the average of
864  // the fine ones -- after the base states have already been built -- leaves the
865  // coarse base state out of balance with the heights the dycore then uses for
866  // vertical gradients, Rayleigh damping and the sponge zones. z_phys_cc is also
867  // derived from z_phys_nd, which is not averaged down either, so averaging only
868  // the cell-centered heights made the two disagree inside the refined region.
869  //
870  // detJ IS still averaged down: AverageDownTo weights (rho S) by detJ_cc before
871  // averaging and divides by it afterwards, so the coarse detJ must be the average
872  // of the fine detJ for that average-down to telescope and stay conservative.
873  for (int crse_lev = lev-1; crse_lev >= 0; crse_lev--) {
874  average_down( *detJ_cc[crse_lev+1], *detJ_cc[crse_lev], 0, 1, refRatio(crse_lev));
875  }
876  }
877 
878  //
879  // Keep the vertical-spacing minimum in step with the new grids, as MakeNewLevelFromCoarse
880  // does. Without this the dt constraint keeps its pre-regrid value on a stretched or
881  // terrain-fitted mesh, where the fine detJ minimum moves with the grids.
882  //
883  if (static_cast<int>(dz_min.size()) <= lev) { dz_min.resize(lev+1); }
884  dz_min[lev] = geom[lev].CellSize(2);
885  if ( (SolverChoice::mesh_type != MeshType::ConstantDz) && detJ_cc[lev] ) {
886  dz_min[lev] *= (*detJ_cc[lev]).min(0);
887  }
888 
889  // ********************************************************************************************
890  // Build the data structures for canopy model (depends upon z_phys)
891  // ********************************************************************************************
893  m_forest_drag[lev]->define_drag_field(ba, dm, geom[lev],
894  z_phys_cc[lev].get(), z_phys_nd[lev].get(),
897  }
898 
899  // *****************************************************************************************************
900  // Create the physbcs objects (after initializing the terrain but before calling FillCoarsePatch
901  // *****************************************************************************************************
902  make_physbcs(lev);
903 
904  // ********************************************************************************************
905  // Update the base state at this level by interpolation from coarser level AND copy
906  // from previous (pre-regrid) base_state array
907  // ********************************************************************************************
908  if (lev > 0) {
909  Interpolater* mapper = &cell_cons_interp;
910 
911  Vector<MultiFab*> fmf = {&base_state[lev ], &base_state[lev ]};
912  Vector<MultiFab*> cmf = {&base_state[lev-1], &base_state[lev-1]};
913  Vector<Real> ftime = {time, time};
914  Vector<Real> ctime = {time, time};
915 
916  // Call FillPatch which ASSUMES that all ghost cells at lev-1 have already been filled
917  FillPatchTwoLevels(temp_base_state, temp_base_state.nGrowVect(), IntVect(0,0,0),
918  time, cmf, ctime, fmf, ftime,
919  0, 0, temp_base_state.nComp(), geom[lev-1], geom[lev],
920  refRatio(lev-1), mapper, domain_bcs_type,
922 
923  // Impose bc's outside the domain
924  (*physbcs_base[lev])(temp_base_state,0,temp_base_state.nComp(),base_state[lev].nGrowVect());
925 
926 #ifdef ERF_USE_NETCDF
927  // *************************************************************************************************
928  // For a WRFInput run, rebuild the base state on the new grids by the SAME construction
929  // used at initialization -- the analytic reference profile from the level-0 parameters,
930  // evaluated at this level's heights, followed by the discrete hydrostatic rebalance --
931  // rather than leaving it as the conservative interpolation of the parent's base state.
932  //
933  // Without this, the first regrid silently replaces a base state that is discretely
934  // hydrostatic on this level's mesh with one that is not, and the level stops agreeing
935  // with its parent in the way it did at t = 0.
936  //
937  // Note this must come AFTER the FillPatchTwoLevels above (which fills the region of
938  // this level outside the fine grids, and the ghost cells) and BEFORE the
939  // FillPatchFineLevel below, which interpolates perturbational quantities relative to
940  // the new base state.
941  // *************************************************************************************************
942  if (solverChoice.init_type == InitType::WRFInput) {
943  rebuild_base_state_from_wrfinput(lev, temp_base_state);
944  (*physbcs_base[lev])(temp_base_state,0,temp_base_state.nComp(),base_state[lev].nGrowVect());
945  }
946 #endif
947 
948  // *************************************************************************************************
949  // This will fill the temporary MultiFabs with data from vars_new
950  // NOTE: the momenta here are only used as scratch space, the momenta themselves are not fillpatched
951  // NOTE: we must create the new base state before calling FillPatch because we will
952  // interpolate perturbational quantities
953  // *************************************************************************************************
954  FillPatchFineLevel(lev, time, {&temp_lev_new[Vars::cons],&temp_lev_new[Vars::xvel],
955  &temp_lev_new[Vars::yvel],&temp_lev_new[Vars::zvel]},
956  {&temp_lev_new[Vars::cons],&rU_new[lev],&rV_new[lev],&rW_new[lev]},
957  base_state[lev], temp_base_state, false);
958  } else {
959  temp_base_state.ParallelCopy(base_state[lev],0,0,base_state[lev].nComp(),
960  base_state[lev].nGrowVect(),base_state[lev].nGrowVect());
961  temp_lev_new[Vars::cons].ParallelCopy(vars_new[lev][Vars::cons],0,0,ncomp_cons,ngrow_state,ngrow_state);
962  temp_lev_new[Vars::xvel].ParallelCopy(vars_new[lev][Vars::xvel],0,0, 1,ngrow_vels,ngrow_vels);
963  temp_lev_new[Vars::yvel].ParallelCopy(vars_new[lev][Vars::yvel],0,0, 1,ngrow_vels,ngrow_vels);
964 
965  temp_lev_new[Vars::zvel].setVal(0.);
966  temp_lev_new[Vars::zvel].ParallelCopy(vars_new[lev][Vars::zvel],0,0, 1,
967  IntVect(ngrow_vels,ngrow_vels,0),IntVect(ngrow_vels,ngrow_vels,0));
968  }
969 
970  // Now swap the pointers since we needed both old and new in the FillPatch
971  std::swap(temp_base_state, base_state[lev]);
972 
973  // ********************************************************************************************
974  // Copy from new into old just in case
975  // ********************************************************************************************
976  MultiFab::Copy(temp_lev_old[Vars::cons],temp_lev_new[Vars::cons],0,0,ncomp_cons,ngrow_state);
977  MultiFab::Copy(temp_lev_old[Vars::xvel],temp_lev_new[Vars::xvel],0,0, 1,ngrow_vels);
978  MultiFab::Copy(temp_lev_old[Vars::yvel],temp_lev_new[Vars::yvel],0,0, 1,ngrow_vels);
979  MultiFab::Copy(temp_lev_old[Vars::zvel],temp_lev_new[Vars::zvel],0,0, 1,IntVect(ngrow_vels,ngrow_vels,0));
980 
981  // ********************************************************************************************
982  // Now swap the pointers
983  // ********************************************************************************************
984  for (int var_idx = 0; var_idx < Vars::NumTypes; ++var_idx) {
985  std::swap(temp_lev_new[var_idx], vars_new[lev][var_idx]);
986  std::swap(temp_lev_old[var_idx], vars_old[lev][var_idx]);
987  }
988 
989  //
990  // Note that t_new = time here is elapsed time
991  //
992  t_new[lev] = time;
993  t_old[lev] = time - bogus_large_value;
994 
995  // ********************************************************************************************
996  // Build the data structures for calculating diffusive/turbulent terms
997  // ********************************************************************************************
998  update_diffusive_arrays(lev, ba, dm);
999 
1000  // ********************************************************************************************
1001  // Thin immersed body -- thin_[xyz]force and [xyz]flux_imask must be re-defined on the new
1002  // (ba,dm), otherwise they still live on the pre-regrid grids
1003  // ********************************************************************************************
1004  init_thin_body(lev, ba, dm);
1005 
1006  //********************************************************************************************
1007  // Microphysics
1008  // *******************************************************************************************
1009  int q_size = micro->Get_Qmoist_Size(lev);
1010  qmoist[lev].resize(q_size);
1011  micro->Define(lev, solverChoice);
1012  if (solverChoice.moisture_type != MoistureType::None)
1013  {
1014  micro->Init(lev, vars_new[lev][Vars::cons],
1015  grids[lev], Geom(lev), zero,
1016  z_phys_nd[lev], detJ_cc[lev]); // dummy dt value
1017  // Refresh the land/water mask pointer. Must be re-issued at every
1018  // micro->Init site: init_stuff rebuilds lmask_lev[lev][0], so a stale
1019  // pointer would dangle. No-op for every scheme except WDM6.
1020  micro->Set_Lmask(lev, (lmask_lev[lev].empty()) ? nullptr : lmask_lev[lev][0].get());
1021  }
1022  for (int mvar(0); mvar<qmoist[lev].size(); ++mvar) {
1023  qmoist[lev][mvar] = micro->Get_Qmoist_Ptr(lev,mvar);
1024  }
1025 
1026  //********************************************************************************************
1027  // Radiation
1028  // *******************************************************************************************
1030  {
1031  rad[lev]->Init(geom[lev], ba, &vars_new[lev][Vars::cons]);
1032  }
1033 
1034  // ********************************************************************************************
1035  // Initialize the integrator class
1036  // ********************************************************************************************
1038 
1039  // ********************************************************************************************
1040  // Re-define every FillPatcher that references this level's grids.
1041  //
1042  // FPr_*[lev-1] caches this level as the FINE side; FPr_*[lev] caches it as the COARSE side
1043  // (see Construct_ERFFillPatchers). A regrid that moves this level invalidates both, but
1044  // only the first was being redefined -- so a finer level whose own grids happened not to
1045  // change kept a coarse-fine fill built against this level's pre-regrid layout, which then
1046  // faults on its first step.
1047  //
1048  // AmrCore::regrid walks coarse to fine and does not clear removed levels until its loop
1049  // finishes, so vars_new[lev+1] is still valid here. If level lev+1 is itself remade
1050  // afterwards it just redefines its own patcher again; Define is idempotent.
1051  // ********************************************************************************************
1052  if ( (cf_width >= 0) && ((ba != ba_old) || (dm != dm_old)) ) {
1053  if (lev > 0) { Define_ERFFillPatchers(lev); }
1054  if ( (lev+1 <= finest_level) && (lev < static_cast<int>(FPr_c.size())) ) {
1055  Define_ERFFillPatchers(lev+1);
1056  }
1057  }
1058 
1059  // ********************************************************************************************
1060  // Re-establish the pressure gradient on an anelastic level.
1061  //
1062  // init_stuff has just re-defined gradp[lev] on the new grids and zeroed it.
1063  // MakeNewLevelFromCoarse recovers it with a projection; without the same call here every
1064  // regrid silently discards the pressure gradient. Must follow the FillPatcher definition
1065  // above, which the projection's coarse-fine fill depends on.
1066  // ********************************************************************************************
1067  if (solverChoice.anelastic[lev]) {
1068  Real dummy_dt = Real(1.0);
1069  project_initial_velocity(lev, time, dummy_dt);
1070  pp_inc[lev].setVal(0.0);
1071  }
1072 
1073  // ********************************************************************************************
1074  // Initialize the 2D data structures
1075  // ********************************************************************************************
1076  // NOTE: 2D MFs must be filled before SurfaceLayer is defined since SL class uses sst/tsk
1077  // Clear the 2D arrays
1078  if (sst_lev[lev][0]) {
1079  for (int n = 0; n < sst_lev[lev].size(); n++) {
1080  sst_lev[lev][n].reset();
1081  }
1082  }
1083  if (tsk_lev[lev][0]) {
1084  for (int n = 0; n < tsk_lev[lev].size(); n++) {
1085  tsk_lev[lev][n].reset();
1086  }
1087  }
1088  if (lat_m[lev]) {
1089  lat_m[lev].reset();
1090  }
1091  if (lon_m[lev]) {
1092  lon_m[lev].reset();
1093  }
1094  if (sinPhi_m[lev]) {
1095  sinPhi_m[lev].reset();
1096  }
1097  if (cosPhi_m[lev]) {
1098  cosPhi_m[lev].reset();
1099  }
1100 
1101  //
1102  // Interpolate the 2D arrays at the lower boundary. We assume that since we created
1103  // them by interpolation it is ok just to recreate them by interpolation.
1104  // Note that ba2d is constructed already in init_stuff, but we have not yet defined dmap[lev]
1105  // so we must explicitly pass dm.
1106  Interp2DArrays(lev,ba2d[lev],dm);
1107 
1108  // ********************************************************************************************
1109  // Land Surface Model
1110  //
1111  // Must precede the SurfaceLayer update below, which caches lsm_data[lev]/lsm_flux[lev] as
1112  // raw MultiFab*. Left out, those pointers survive the regrid on the OLD (ba,dm) and are
1113  // then indexed with an MFIter over the new grids -- an out-of-bounds device read on the
1114  // level's first step after the regrid.
1115  //
1116  // At lev > 0 this is cheap and lossless: NOAHMP::Advance calls interp_from_lev0 every
1117  // step, so a fine level carries no prognostic state of its own. At level 0 the state
1118  // lives in the per-box Fortran NoahmpIO_type objects, which Init() rebuilds from the land
1119  // file and for which no redistribution onto a new decomposition exists -- so refuse
1120  // rather than silently cold-start the soil column. (Level 0 is only ever remade from
1121  // ERF::restart, not from regrid, which starts at lbase+1.)
1122  // ********************************************************************************************
1124  !((lev == 0) && (solverChoice.lsm_type == LandSurfaceType::NOAHMP)),
1125  "RemakeLevel at level 0 would cold-start the Noah-MP soil state: "
1126  "NoahmpIO_type redistribution onto a new DistributionMapping is not implemented");
1127 
1128  make_lsm_at_level(lev, true); // from_regrid=true: always initialize LSM during regrid
1129 
1130  //
1131  // A level-0 remake replaces the MultiFabs that every finer level's model caches for
1132  // interp_from_lev0, so re-issue those pointers all the way up.
1133  //
1134  if (lev == 0) {
1135  for (int k = 1; k <= finest_level; ++k) {
1138  }
1139  }
1140 
1141  // ********************************************************************************************
1142  // Update the SurfaceLayer arrays at this level
1143  // ********************************************************************************************
1144  for (OrientationIter oit; oit; ++oit) {
1145  Orientation ori = oit();
1146  if (m_SurfaceLayer[ori] != nullptr) {
1147  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
1148  int nlevs = finest_level+1;
1149  Vector<MultiFab*> mfv_old = {&vars_old[lev][Vars::cons], &vars_old[lev][Vars::xvel],
1150  &vars_old[lev][Vars::yvel], &vars_old[lev][Vars::zvel]};
1151  m_SurfaceLayer[ori]->make_SurfaceLayer_at_level(lev,nlevs,
1152  mfv_old, Theta_prim[lev], Qv_prim[lev],
1153  Qr_prim[lev], z_phys_nd[lev],
1154  Hwave[lev].get(),Lwave[lev].get(),eddyDiffs_lev[lev].get(),
1156  sst_lev[lev], tsk_lev[lev], lmask_lev[lev]);
1157  }
1158  }
1159  }
1160 
1161  // ********************************************************************************************
1162  // Set up the Rayleigh damping vectors at this (new) level
1163  // ********************************************************************************************
1166  {
1167  initRayleigh_at_level(lev);
1168  }
1169 
1170  // Particle redistribute handled in timeStep() after regrid() completes.
1171  // Calling it here causes stale-grid crashes.
1172 }
void remake_zphys(int lev, amrex::Real time, std::unique_ptr< amrex::MultiFab > &temp_zphys_nd)
Definition: ERF_MakeNewArrays.cpp:1090
Here is the call graph for this function:

◆ ResetIntervalMeans()

void ERF::ResetIntervalMeans ( )
359 {
360  for (int lev = 0; lev <= finest_level; ++lev) {
361  if (interval_means[lev] != nullptr) {
362  interval_means[lev]->setVal(zero);
363  t_mean_cnt[lev] = 0.0;
364  }
365  }
366 }

◆ ResetIntervalMeansAfter3DPlotfileBatch()

void ERF::ResetIntervalMeansAfter3DPlotfileBatch ( int  plotfiles_written,
bool  interval_diagnostic_consumed 
)
346 {
348  plotfiles_written, interval_diagnostic_consumed,
351  return;
352  }
353 
355 }
bool plot3d_batch_resets_interval_means(int plotfiles_written, bool interval_diagnostic_consumed, bool compute_mean_vars, const std::string &reset_mode)
Definition: ERF_PlotfileSelection.H:95
Here is the call graph for this function:

◆ resolve_station_positions()

void ERF::resolve_station_positions ( )
807 {
808  auto& stations = station_sampler->stations();
809 
810  bool any_latlon = false;
811  for (const auto& station : stations) {
812  for (const auto& loc : station.locs) { any_latlon = any_latlon || loc.use_latlon; }
813  }
814 
815  const auto problo = geom[0].ProbLoArray();
816  const auto probhi = geom[0].ProbHiArray();
817  const auto dx0 = geom[0].CellSizeArray();
818  const Box& dom0 = geom[0].Domain();
819 
820  if (any_latlon)
821  {
822  if (lat_m[0] == nullptr || lon_m[0] == nullptr) {
823  Abort("Station output: .lat/.long need a run with latitude/longitude arrays "
824  "(a WRF or metgrid initialization, or a restart from one). Place the "
825  "stations with .x and .y in domain coordinates instead");
826  }
827 
828  // Gather the level-0 mass-point latitude and longitude onto the IO rank.
829  // This is a setup-time, level-0, 2D array, so the gather is affordable and
830  // keeps the search and the inverse map as plain host code.
831  //
832  // The ceiling is one rank holding 2 Reals per level-0 cell -- 100 MB or so
833  // for the largest domains ERF is run on, a few hundred million cells -- and
834  // a search of that array per station location, once. If either ever
835  // matters, the search is what to distribute: the array is gathered once
836  // for all the locations, but each location scans all of it.
837  //
838  // NOTE: every init path fills lat_m / lon_m with mass-point values --
839  // init_from_wrfinput reads WRF's XLAT / XLONG and init_from_metgrid
840  // reads XLAT_M / XLONG_M -- so no averaging is needed here.
841  MultiFab latlon(ba2d[0], dmap[0], 2, 0);
842  for (MFIter mfi(latlon, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
843  const Box& bx = mfi.tilebox();
844  const Array4<Real>& ll = latlon.array(mfi);
845  const Array4<const Real>& lat = lat_m[0]->const_array(mfi);
846  const Array4<const Real>& lon = lon_m[0]->const_array(mfi);
847  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
848  {
849  ll(i,j,k,0) = lat(i,j,0);
850  ll(i,j,k,1) = lon(i,j,0);
851  });
852  }
853 
854  Box dom2d(dom0); dom2d.setRange(2,0);
855  BoxArray ba_one(dom2d);
856  Vector<int> pmap(1, ParallelDescriptor::IOProcessorNumber());
857  DistributionMapping dm_one(pmap);
858  MultiFab latlon_all(ba_one, dm_one, 2, 0, MFInfo().SetArena(The_Pinned_Arena()));
859  latlon_all.ParallelCopy(latlon, 0, 0, 2);
860 
861  // The copy is device-side in a GPU build; the search below is host code
862  Gpu::streamSynchronize();
863 
864  const int ilo = dom0.smallEnd(0), ihi = dom0.bigEnd(0);
865  const int jlo = dom0.smallEnd(1), jhi = dom0.bigEnd(1);
866 
867  for (auto& station : stations)
868  {
869  for (auto& loc : station.locs)
870  {
871  if (!loc.use_latlon) { continue; }
872 
873  Real out[4] = {Real(0.0), Real(0.0), Real(0.0), Real(0.0)};
874 
875  if (ParallelDescriptor::IOProcessor())
876  {
877  const Array4<const Real>& ll = latlon_all.const_array(0);
878 
879  auto cosfac = std::cos(loc.req_lat * PI / Real(180.0));
880  auto dist2 = [&](int i, int j) {
881  const Real dlat = ll(i,j,0,0) - loc.req_lat;
882  const Real dlon = (ll(i,j,0,1) - loc.req_lon) * cosfac;
883  return dlat*dlat + dlon*dlon;
884  };
885 
886  int bi = ilo, bj = jlo;
887  Real best = dist2(ilo,jlo);
888  for (int j = jlo; j <= jhi; ++j) {
889  for (int i = ilo; i <= ihi; ++i) {
890  const Real d = dist2(i,j);
891  if (d < best) { best = d; bi = i; bj = j; }
892  }
893  }
894 
895  // One linear solve in index space inverts the map: over a cell
896  // the projection is linear to well below a cell width.
897  const int im = std::max(bi-1, ilo), ip = std::min(bi+1, ihi);
898  const int jm = std::max(bj-1, jlo), jp = std::min(bj+1, jhi);
899  const Real inv_di = (ip > im) ? Real(1.0)/Real(ip-im) : Real(0.0);
900  const Real inv_dj = (jp > jm) ? Real(1.0)/Real(jp-jm) : Real(0.0);
901 
902  const Real dlat_di = (ll(ip,bj,0,0) - ll(im,bj,0,0)) * inv_di;
903  const Real dlon_di = (ll(ip,bj,0,1) - ll(im,bj,0,1)) * inv_di;
904  const Real dlat_dj = (ll(bi,jp,0,0) - ll(bi,jm,0,0)) * inv_dj;
905  const Real dlon_dj = (ll(bi,jp,0,1) - ll(bi,jm,0,1)) * inv_dj;
906 
907  const Real rlat = loc.req_lat - ll(bi,bj,0,0);
908  const Real rlon = loc.req_lon - ll(bi,bj,0,1);
909  Real di = Real(0.0), dj = Real(0.0);
910  if (!invert_latlon_offset(rlat, rlon, dlat_di, dlon_di, dlat_dj, dlon_dj, di, dj)) {
911  Abort("Station '" + station.name + "': the latitude/longitude arrays are "
912  "degenerate near the requested point, so it cannot be inverted");
913  }
914 
915  // The solve places the station relative to the nearest grid
916  // point, so more than a cell away means the request cannot be
917  // trusted: either it is outside the domain, or the lat/lon map
918  // is too poorly conditioned near it for one linear solve.
919  if (std::abs(di) > Real(1.0) || std::abs(dj) > Real(1.0)) {
920  Abort("Station '" + station.name + "': requested lat=" +
921  std::to_string(loc.req_lat) + " long=" + std::to_string(loc.req_lon) +
922  " resolved to a point more than one cell from the nearest grid "
923  "point (lat=" + std::to_string(ll(bi,bj,0,0)) + " long=" +
924  std::to_string(ll(bi,bj,0,1)) + "), which means it is outside the "
925  "domain, or that the latitude/longitude arrays are too distorted "
926  "near it to invert");
927  }
928 
929  out[0] = problo[0] + (Real(bi) + Real(0.5) + di) * dx0[0];
930  out[1] = problo[1] + (Real(bj) + Real(0.5) + dj) * dx0[1];
931  out[2] = ll(bi,bj,0,0) + dlat_di*di + dlat_dj*dj;
932  out[3] = ll(bi,bj,0,1) + dlon_di*di + dlon_dj*dj;
933  }
934 
935  ParallelDescriptor::Bcast(out, 4, ParallelDescriptor::IOProcessorNumber());
936  loc.x = out[0]; loc.y = out[1]; loc.got_lat = out[2]; loc.got_lon = out[3];
937  }
938  }
939  }
940 
941  for (const auto& station : stations) {
942  for (const auto& loc : station.locs) {
943  if (loc.x < problo[0] || loc.x > probhi[0] ||
944  loc.y < problo[1] || loc.y > probhi[1]) {
945  Abort("Station '" + station.name + "': x=" + std::to_string(loc.x) +
946  " y=" + std::to_string(loc.y) + " is outside the problem domain");
947  }
948  }
949  }
950 }
auto probhi
Definition: ERF_InitCustomPertVels_ABL.H:37
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
bool invert_latlon_offset(amrex::Real dlat, amrex::Real dlon, amrex::Real dlat_di, amrex::Real dlon_di, amrex::Real dlat_dj, amrex::Real dlon_dj, amrex::Real &di, amrex::Real &dj)
Definition: ERF_StationSampler.H:59
real(c_double), private di
Definition: ERF_module_mp_morr_two_moment.F90:203
Here is the call graph for this function:

◆ resolve_station_stencils()

void ERF::resolve_station_stencils ( )
969 {
970  // What this computes -- which level supplies each location, which four cells
971  // its horizontal stencil reads, and how far up the column -- is a function of
972  // the grids and of the terrain under the station, and it costs a ParallelCopy,
973  // a stream synchronization and a broadcast to compute. Neither input changes
974  // between samples unless the grids do, so the answer already in the locations
975  // stands and none of that work is needed.
976  //
977  // The exception is a mesh whose terrain moves: there the column heights change
978  // under a fixed grid, so the level a height falls in can change with the grids
979  // unchanged and the stencils have to be resolved every time.
980  const bool terrain_moves = (solverChoice.terrain_type == TerrainType::MovingFittedMesh);
981  if (!terrain_moves && station_sampler->stencilsValidFor(grids, finest_level)) { return; }
982 
983  auto& stations = station_sampler->stations();
984 
985  // Flat list of (station, location) pairs, so a stencil and the location it
986  // belongs to share an index.
987  Vector<std::pair<int,int>> all_locs;
988  for (int is = 0; is < static_cast<int>(stations.size()); ++is) {
989  for (int il = 0; il < static_cast<int>(stations[is].locs.size()); ++il) {
990  all_locs.emplace_back(is, il);
991  }
992  }
993  const int nloc = static_cast<int>(all_locs.size());
994 
995  // ---- the horizontal stencil, and the coverage above it, on every level ----
996  Vector<Vector<StationLevelStencil>> cand(nloc, Vector<StationLevelStencil>(finest_level+1));
997 
998  for (int lev = 0; lev <= finest_level; ++lev)
999  {
1000  const auto problo = geom[lev].ProbLoArray();
1001  const auto dx = geom[lev].CellSizeArray();
1002  const Box& dom = geom[lev].Domain();
1003 
1004  const int ilo = dom.smallEnd(0), ihi = dom.bigEnd(0);
1005  const int jlo = dom.smallEnd(1), jhi = dom.bigEnd(1);
1006  const int klo = dom.smallEnd(2), khi = dom.bigEnd(2);
1007 
1008  // How far up this level's valid region covers the column at (i,j) --
1009  // after wrapping, where the direction is periodic -- counting only
1010  // coverage that is contiguous from the bottom of the domain. Returns
1011  // klo-1 when even the bottom cell is not covered.
1012  auto column_top = [&](int i, int j)
1013  {
1014  int ii = i, jj = j;
1015  if (geom[lev].isPeriodic(0)) {
1016  const int nx = dom.length(0);
1017  ii = ilo + ((i - ilo) % nx + nx) % nx;
1018  }
1019  if (geom[lev].isPeriodic(1)) {
1020  const int ny = dom.length(1);
1021  jj = jlo + ((j - jlo) % ny + ny) % ny;
1022  }
1023  if (ii < ilo || ii > ihi || jj < jlo || jj > jhi) { return klo-1; }
1024 
1025  std::vector<std::pair<int,Box>> isects;
1026  grids[lev].intersections(Box(IntVect(ii,jj,klo), IntVect(ii,jj,khi)), isects);
1027 
1028  // The pieces of the column come back in no particular order, so grow
1029  // the covered span from the bottom until nothing else abuts it.
1030  int ktop = klo-1;
1031  bool grew = true;
1032  while (grew) {
1033  grew = false;
1034  for (const auto& is : isects) {
1035  if (is.second.smallEnd(2) <= ktop+1 && is.second.bigEnd(2) > ktop) {
1036  ktop = is.second.bigEnd(2);
1037  grew = true;
1038  }
1039  }
1040  }
1041  return ktop;
1042  };
1043 
1044  for (int ip = 0; ip < nloc; ++ip)
1045  {
1046  const auto& loc = stations[all_locs[ip].first].locs[all_locs[ip].second];
1047  auto& c = cand[ip][lev];
1048 
1049  int ic = ilo + static_cast<int>(std::floor((loc.x - problo[0]) / dx[0]));
1050  int jc = jlo + static_cast<int>(std::floor((loc.y - problo[1]) / dx[1]));
1051  ic = std::min(std::max(ic, ilo), ihi);
1052  jc = std::min(std::max(jc, jlo), jhi);
1053 
1054  const Real tx = (loc.x - (problo[0] + (Real(ic-ilo) + Real(0.5))*dx[0])) / dx[0];
1055  const Real ty = (loc.y - (problo[1] + (Real(jc-jlo) + Real(0.5))*dx[1])) / dx[1];
1056 
1057  int i0 = (tx >= Real(0.0)) ? ic : ic-1;
1058  int j0 = (ty >= Real(0.0)) ? jc : jc-1;
1059  Real wx = (tx >= Real(0.0)) ? tx : tx + Real(1.0);
1060  Real wy = (ty >= Real(0.0)) ? ty : ty + Real(1.0);
1061  int i1 = i0 + 1;
1062  int j1 = j0 + 1;
1063 
1064  // Within the outer half cell of a non-periodic boundary there is
1065  // no second cell to interpolate from, so the stencil collapses to
1066  // the edge cell.
1067  if (!geom[lev].isPeriodic(0)) {
1068  if (i0 < ilo) { i0 = i1 = ilo; wx = Real(0.0); }
1069  if (i1 > ihi) { i0 = i1 = ihi; wx = Real(0.0); }
1070  }
1071  if (!geom[lev].isPeriodic(1)) {
1072  if (j0 < jlo) { j0 = j1 = jlo; wy = Real(0.0); }
1073  if (j1 > jhi) { j0 = j1 = jhi; wy = Real(0.0); }
1074  }
1075 
1076  c.ic = ic; c.jc = jc;
1077  c.i0 = i0; c.j0 = j0;
1078  c.i1 = i1; c.j1 = j1;
1079  c.wx = wx; c.wy = wy;
1080  c.kcov = std::min(std::min(column_top(i0,j0), column_top(i1,j0)),
1081  std::min(column_top(i0,j1), column_top(i1,j1)));
1082  }
1083  }
1084 
1085  // ---- the cell centre heights of the candidate columns, on the IO rank ----
1086  //
1087  // Which cells the vertical interpolation reads depends on the terrain under
1088  // the station, so choosing the level needs the heights themselves. They are
1089  // one component of a 2x2 column, so they are gathered where the choice is
1090  // made, on the same rank that later interpolates.
1091  //
1092  const int io_rank = ParallelDescriptor::IOProcessorNumber();
1093 
1094  Vector<Vector<int>> lev_cand(finest_level+1); // ip of each gathered box, in box order
1095  Vector<MultiFab> zc(finest_level+1), znd(finest_level+1);
1096 
1097  for (int lev = 0; lev <= finest_level; ++lev)
1098  {
1099  const Box& dom = geom[lev].Domain();
1100  const int klo = dom.smallEnd(2);
1101 
1102  for (int ip = 0; ip < nloc; ++ip) {
1103  if (cand[ip][lev].kcov >= klo) { lev_cand[lev].push_back(ip); }
1104  }
1105  // Terrain gives both arrays or neither: z_of_k below reads z_phys_cc and
1106  // falls back to a uniform dz without it, while z_surf reads z_phys_nd, so
1107  // a level with one and not the other would measure a height from the
1108  // terrain and then look it up on a flat column. No init path produces
1109  // that, and this says so rather than leaving it to be discovered.
1110  AMREX_ALWAYS_ASSERT((z_phys_cc[lev] != nullptr) == (z_phys_nd[lev] != nullptr));
1111  if (lev_cand[lev].empty() || !z_phys_cc[lev]) { continue; }
1112 
1113  // Periodicity restricted to the horizontal: the columns start at the
1114  // bottom of the domain, so there is nothing to wrap in z.
1115  const Periodicity period(IntVect(geom[lev].isPeriodic(0) ? dom.length(0) : 0,
1116  geom[lev].isPeriodic(1) ? dom.length(1) : 0,
1117  0));
1118 
1119  BoxList bl_cc, bl_nd;
1120  for (const int ip : lev_cand[lev]) {
1121  const auto& c = cand[ip][lev];
1122  bl_cc.push_back(Box(IntVect(c.i0,c.j0,klo), IntVect(c.i1,c.j1,c.kcov)));
1123  bl_nd.push_back(Box(IntVect(c.i0,c.j0,klo), IntVect(c.i1+1,c.j1+1,klo),
1124  IntVect::TheNodeVector()));
1125  }
1126 
1127  Vector<int> pmap(static_cast<int>(bl_cc.size()), io_rank);
1128  DistributionMapping dm_io(pmap);
1129  MFInfo pinned = MFInfo().SetArena(The_Pinned_Arena());
1130 
1131  if (z_phys_cc[lev]) {
1132  zc[lev].define(BoxArray(bl_cc), dm_io, 1, 0, pinned);
1133  zc[lev].setVal(Real(0.0));
1134  zc[lev].ParallelCopy(*z_phys_cc[lev], 0, 0, 1, IntVect(0), IntVect(0), period);
1135  }
1136  if (z_phys_nd[lev]) {
1137  znd[lev].define(BoxArray(bl_nd), dm_io, 1, 0, pinned);
1138  znd[lev].setVal(Real(0.0));
1139  znd[lev].ParallelCopy(*z_phys_nd[lev], 0, 0, 1, IntVect(0), IntVect(0), period);
1140  }
1141  }
1142 
1143  // The gathers above are device-side in a GPU build, and the choice below
1144  // reads the pinned destinations on the host.
1145  Gpu::streamSynchronize();
1146 
1147  // ---- choose the level, on the IO rank, and tell everyone ----
1148  //
1149  // Only the level and the top of the column have to be broadcast: every rank
1150  // computed the same stencils above.
1151  //
1152  Vector<int> chosen(2*nloc, -1);
1153 
1154  if (ParallelDescriptor::IOProcessor())
1155  {
1156  for (int ip = 0; ip < nloc; ++ip)
1157  {
1158  const Station& station = stations[all_locs[ip].first];
1159 
1160  // A station of 2D variables only never leaves the bottom of the column
1161  const bool reads_column = station.has_3d_vars() && !station.heights.empty();
1162 
1163  for (int lev = finest_level; lev >= 0 && chosen[2*ip] < 0; --lev)
1164  {
1165  const auto& c = cand[ip][lev];
1166 
1167  const auto problo = geom[lev].ProbLoArray();
1168  const auto dx = geom[lev].CellSizeArray();
1169  const Box& dom = geom[lev].Domain();
1170  const int klo = dom.smallEnd(2);
1171  const int khi = dom.bigEnd(2);
1172 
1173  if (c.kcov < klo) { continue; }
1174 
1175  if (!reads_column) {
1176  chosen[2*ip] = lev; chosen[2*ip+1] = klo;
1177  break;
1178  }
1179 
1180  const int ib = static_cast<int>(
1181  std::find(lev_cand[lev].begin(), lev_cand[lev].end(), ip) - lev_cand[lev].begin());
1182 
1183  auto bilinear = [&](const Array4<const Real>& a, int k)
1184  {
1185  return (Real(1.0)-c.wy) * ( (Real(1.0)-c.wx)*a(c.i0,c.j0,k) + c.wx*a(c.i1,c.j0,k) )
1186  + c.wy * ( (Real(1.0)-c.wx)*a(c.i0,c.j1,k) + c.wx*a(c.i1,c.j1,k) );
1187  };
1188 
1189  Array4<const Real> zc_arr;
1190  if (z_phys_cc[lev]) { zc_arr = zc[lev].const_array(ib); }
1191  auto z_of_k = [&](int k)
1192  {
1193  if (zc_arr) { return bilinear(zc_arr, k); }
1194  return problo[2] + (Real(k-klo) + Real(0.5)) * dx[2];
1195  };
1196 
1197  // Terrain elevation under the station, which is where an AGL
1198  // height is measured from
1199  Real z_surf = problo[2];
1200  if (z_phys_nd[lev]) {
1201  const Array4<const Real>& nd = znd[lev].const_array(ib);
1202  auto corner_avg = [&](int i, int j) {
1203  return Real(0.25) * (nd(i,j,klo) + nd(i+1,j,klo) + nd(i,j+1,klo) + nd(i+1,j+1,klo));
1204  };
1205  z_surf = (Real(1.0)-c.wy) * ( (Real(1.0)-c.wx)*corner_avg(c.i0,c.j0) + c.wx*corner_avg(c.i1,c.j0) )
1206  + c.wy * ( (Real(1.0)-c.wx)*corner_avg(c.i0,c.j1) + c.wx*corner_avg(c.i1,c.j1) );
1207  }
1208 
1209  // Every requested height must be bracketed within the covered
1210  // part of the column. Running out of coverage is fatal to the
1211  // level unless the coverage reaches the top of the domain, where
1212  // the interpolation legitimately clamps.
1213  // The zero of the requested heights: the terrain under the
1214  // station, or the bottom of the domain.
1215  const Real z_zero = station.heights_are_agl ? z_surf : Real(0.0);
1216 
1217  int ktop = klo;
1218  bool usable = true;
1219  bool below_first_cell = false;
1220  for (const Real height : station.heights)
1221  {
1222  const Real z_target = z_zero + height;
1223  if (z_of_k(c.kcov) < z_target && c.kcov < khi) { usable = false; break; }
1224 
1225  if (z_target < z_of_k(klo)) { below_first_cell = true; }
1226 
1227  int kk = klo;
1228  while (kk < c.kcov && z_of_k(kk+1) < z_target) { ++kk; }
1229  ktop = std::max(ktop, std::min(kk+1, c.kcov));
1230  }
1231 
1232  if (usable) {
1233  chosen[2*ip] = lev;
1234  chosen[2*ip+1] = ktop;
1235 
1236  // Nothing in the model lives between the terrain and the first
1237  // cell centre, so a height there is the first cell centre's
1238  // value. That is a trap for exactly the heights an observation
1239  // comparison asks for, so say so, once.
1240  if (below_first_cell && !station_sampler->lowHeightWarned()) {
1241  station_sampler->setLowHeightWarned();
1242  Warning("Station '" + station.name + "': a requested height is below the "
1243  "first cell centre of the level it is sampled from, so it is "
1244  "reported as the value at that cell centre -- there is no "
1245  "similarity extrapolation to the requested height. For 2 m or "
1246  "10 m quantities, request the 2D diagnostics (temperature_2m, "
1247  "water_vapor_mixing_ratio_2m, and the surface-layer diagnostics) "
1248  "instead of a 3D variable at that height");
1249  }
1250  }
1251  }
1252  }
1253  }
1254 
1255  ParallelDescriptor::Bcast(chosen.data(), chosen.size(), io_rank);
1256 
1257  for (int ip = 0; ip < nloc; ++ip)
1258  {
1259  // Level 0 covers the whole domain from the ground up, so a station
1260  // inside the domain always resolves.
1261  AMREX_ALWAYS_ASSERT(chosen[2*ip] >= 0);
1262 
1263  auto& loc = stations[all_locs[ip].first].locs[all_locs[ip].second];
1264  const auto& c = cand[ip][chosen[2*ip]];
1265 
1266  loc.lev = chosen[2*ip];
1267  loc.ic = c.ic; loc.jc = c.jc;
1268  loc.i0 = c.i0; loc.j0 = c.j0;
1269  loc.i1 = c.i1; loc.j1 = c.j1;
1270  loc.wx = c.wx; loc.wy = c.wy;
1271  loc.ktop = chosen[2*ip+1];
1272  }
1273 
1274  station_sampler->rememberStencilGrids(grids, finest_level);
1275 
1276  // Which level a station is sampled from follows from the grids rather than
1277  // from anything in the inputs file, so report it once.
1278  if (verbose > 0 && !station_sampler->levelsReported())
1279  {
1280  station_sampler->setLevelsReported();
1281  for (int ip = 0; ip < nloc; ++ip) {
1282  const Station& station = stations[all_locs[ip].first];
1283  const StationLoc& loc = station.locs[all_locs[ip].second];
1284  Print() << "Station output: '" << station.name << "' at x=" << loc.x
1285  << " y=" << loc.y << " is sampled from level " << loc.lev
1286  << " (cell " << loc.ic << "," << loc.jc
1287  << ", column through k=" << loc.ktop << ")" << std::endl;
1288  }
1289  }
1290 }
const Real zc
Definition: ERF_InitCustomPert_ABL.H:56
Real height
Definition: ERF_InitCustomPert_SquallLine.H:33
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real column_top(const int *col_top, int ci, int cj, int i0, int j0, int bw, int bh, amrex::Real z_ground, amrex::Real dz)
Definition: ERF_IBSEBSolar.H:163
Definition: ERF_StationSampler.H:86
int jc
Definition: ERF_StationSampler.H:105
int ktop
Definition: ERF_StationSampler.H:112
amrex::Real x
Definition: ERF_StationSampler.H:94
int ic
Definition: ERF_StationSampler.H:104
amrex::Real y
Definition: ERF_StationSampler.H:95
int lev
Definition: ERF_StationSampler.H:103
Definition: ERF_StationSampler.H:133
amrex::Vector< StationLoc > locs
Definition: ERF_StationSampler.H:136
bool has_3d_vars() const
Definition: ERF_StationSampler.H:147
bool heights_are_agl
Definition: ERF_StationSampler.H:142
std::string name
Definition: ERF_StationSampler.H:134
amrex::Vector< amrex::Real > heights
Definition: ERF_StationSampler.H:141
Here is the call graph for this function:

◆ restart()

void ERF::restart ( )
2343 {
2344  auto dRestartTime0 = amrex::second();
2345 
2347 
2348 #ifdef ERF_USE_NETCDF
2349  //
2350  // The checkpoint carries every level's base state, but not the reference parameters
2351  // it was built from. Recover them now, before anything can regrid: the first regrid
2352  // that creates or remakes a refined level rebuilds that level's base state from them.
2353  //
2354  restore_base_state_params_on_restart();
2355 #endif
2356 
2357  // Force regrid on level 0 if more procs than boxes are requested
2359  grids[0].size() < ParallelDescriptor::NProcs() );
2360 
2362  //
2363  // Coarsening before we split the grids ensures that each resulting
2364  // grid will have an even number of cells in each direction.
2365  //
2366  BoxArray new_ba(amrex::coarsen(Geom(0).Domain(),2));
2367  //
2368  // Now split up into list of grids within max_grid_size[0] limit.
2369  //
2370  new_ba.maxSize(max_grid_size[0]/2);
2371  //
2372  // Now refine these boxes back to level zero
2373  //
2374  new_ba.refine(2);
2375 
2376  if (refine_grid_layout) {
2377  ChopGrids(0, new_ba, ParallelDescriptor::NProcs());
2378  }
2379 
2380  if (new_ba != grids[0]) {
2381  DistributionMapping new_dm(new_ba);
2382  RemakeLevel(0,static_cast<Real>(t_new[0]),new_ba,new_dm);
2383  }
2384  }
2385 
2386 #ifdef ERF_USE_PARTICLES
2387  // We call this here without knowing whether the particles have already been initialized or not
2388  initializeTracers((ParGDBBase*)GetParGDB(),z_phys_nd,t_new[0]);
2389 #endif
2390 
2391  double cur_time = t_new[0];
2392  if (m_check_per > zero) {last_check_file_time = std::floor(cur_time/m_check_per ) * m_check_per;}
2393  if (m_plot2d_per_1 > zero) {last_plot2d_file_time_1 = std::floor(cur_time/m_plot2d_per_1) * m_plot2d_per_1;}
2394  if (m_plot2d_per_2 > zero) {last_plot2d_file_time_2 = std::floor(cur_time/m_plot2d_per_2) * m_plot2d_per_2;}
2395  if (m_plot3d_per_1 > zero) {last_plot3d_file_time_1 = std::floor(cur_time/m_plot3d_per_1) * m_plot3d_per_1;}
2396  if (m_plot3d_per_2 > zero) {last_plot3d_file_time_2 = std::floor(cur_time/m_plot3d_per_2) * m_plot3d_per_2;}
2397 
2398  for (int i = 0; i < m_subvol_per.size(); i++) {
2399  if (m_subvol_per[i] > zero) {last_subvol_time[i] = std::floor(cur_time/m_subvol_per[i]) * m_subvol_per[i];}
2400  }
2401 
2407 
2408  for (int i = 0; i < m_subvol_int.size(); i++) {
2409  if (m_subvol_int[i] > 0) {last_subvol_step[i] = istep[0];}
2410  }
2411 
2412  if (verbose > 0)
2413  {
2414  auto dRestartTime = amrex::second() - dRestartTime0;
2415  ParallelDescriptor::ReduceRealMax(dRestartTime,ParallelDescriptor::IOProcessorNumber());
2416  amrex::Print() << "Restart time = " << dRestartTime << " seconds." << '\n';
2417  }
2418 }
void RemakeLevel(int lev, amrex::Real time, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm) override
Definition: ERF_MakeNewLevel.cpp:700
void ReadCheckpointFile()
Definition: ERF_Checkpoint.cpp:823

◆ sample_lines()

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

◆ sample_points()

void ERF::sample_points ( int  lev,
double  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
538 {
539  int ifile = 0;
540 
541  //
542  // Sample the data at a single point in space
543  //
544  int ncomp = mf.nComp();
545  Vector<Real> my_point = get_cell_data(mf, cell);
546 
547  if (!my_point.empty()) {
548 
549  // HERE DO WHATEVER YOU WANT TO THE DATA BEFORE WRITING
550 
551  std::ostream& sample_log = SamplePointLog(ifile);
552  if (sample_log.good()) {
553  sample_log << std::setw(datwidth) << time;
554  for (int i = 0; i < ncomp; ++i)
555  {
556  sample_log << std::setw(datwidth) << my_point[i];
557  }
558  sample_log << std::endl;
559  } // if good
560  } // only write from processor that holds the cell
561 }
AMREX_FORCE_INLINE std::ostream & SamplePointLog(int i)
Definition: ERF.H:1973

◆ sample_stations()

void ERF::sample_stations ( amrex::Real  time)
1302 {
1303  if (!station_sampler || station_sampler->empty()) { return; }
1304 
1305  BL_PROFILE("ERF::sample_stations()");
1306 
1307  auto& ss = *station_sampler;
1308  auto& stations = ss.stations();
1309 
1311 
1312  const int n3d = static_cast<int>(station_vars_3d.size());
1313  const int n2d = static_cast<int>(station_vars_2d.size());
1314 
1315  // ---- fill the plot variables, on the levels that host a station ----
1316  Vector<int> lev_has_station(finest_level+1, 0);
1317  int top_station_lev = 0;
1318  for (const auto& station : stations) {
1319  for (const auto& loc : station.locs) {
1320  lev_has_station[loc.lev] = 1;
1321  top_station_lev = std::max(top_station_lev, loc.lev);
1322  }
1323  }
1324 
1325  Vector<MultiFab> mf3d(finest_level+1);
1326  Vector<MultiFab> mf2d(finest_level+1);
1327 
1328  if (n3d > 0) {
1329  // The scratch has to be built for every level up to the finest one a
1330  // station is on, not just the levels that host a station: the vorticity
1331  // path fills level l from level l-1. Levels above that are not read, so
1332  // they are not built. The sampler runs at every output step, so it also
1333  // asks BuildPlot3DScratch not to average the microphysics state down:
1334  // turning station output on must not change the answer.
1335  Plot3DScratch scratch;
1336  BuildPlot3DScratch(station_vars_3d, scratch, top_station_lev, false);
1337  for (int lev = 0; lev <= finest_level; ++lev) {
1338  if (!lev_has_station[lev]) { continue; }
1339  mf3d[lev].define(grids[lev], dmap[lev], n3d, 0);
1340  FillPlot3DVars(lev, station_vars_3d, scratch, mf3d[lev], n3d, time);
1341  }
1342  }
1343 
1344  if (n2d > 0) {
1345  const auto descriptors =
1348  solverChoice);
1349  for (int lev = 0; lev <= finest_level; ++lev) {
1350  if (!lev_has_station[lev]) { continue; }
1351  mf2d[lev].define(ba2d[lev], dmap[lev], n2d, 0);
1352  FillPlot2DVars(lev, station_vars_2d, station_vars_2d, descriptors, mf2d[lev], n2d);
1353  }
1354  }
1355 
1356  // ---- gather the stencils onto the IO rank ----
1357  const int nstation = static_cast<int>(stations.size());
1358 
1359  // Flat list of (station, location) pairs, so a stencil box and the location
1360  // it belongs to share an index.
1361  Vector<std::pair<int,int>> all_locs;
1362  for (int is = 0; is < nstation; ++is) {
1363  for (int il = 0; il < static_cast<int>(stations[is].locs.size()); ++il) {
1364  all_locs.emplace_back(is, il);
1365  }
1366  }
1367 
1368  const int io_rank = ParallelDescriptor::IOProcessorNumber();
1369 
1370  // Per level: the stencil boxes, in all_locs order, of the locations on it
1371  Vector<Vector<int>> lev_locs(finest_level+1);
1372  for (int ip = 0; ip < static_cast<int>(all_locs.size()); ++ip) {
1373  const auto& loc = stations[all_locs[ip].first].locs[all_locs[ip].second];
1374  lev_locs[loc.lev].push_back(ip);
1375  }
1376 
1377  // Stencil data brought to the IO rank, indexed by the position within lev_locs
1378  Vector<MultiFab> st3d(finest_level+1), st2d(finest_level+1);
1379  Vector<MultiFab> stzc(finest_level+1), stznd(finest_level+1);
1380 
1381  for (int lev = 0; lev <= finest_level; ++lev)
1382  {
1383  if (lev_locs[lev].empty()) { continue; }
1384 
1385  const Box& dom = geom[lev].Domain();
1386  const int klo = dom.smallEnd(2);
1387 
1388  // Periodicity restricted to the horizontal: the stencils start at the
1389  // bottom of the domain, so there is nothing to wrap in z.
1390  const Periodicity period(IntVect(geom[lev].isPeriodic(0) ? dom.length(0) : 0,
1391  geom[lev].isPeriodic(1) ? dom.length(1) : 0,
1392  0));
1393 
1394  // Each stencil reaches only as far up as resolve_station_stencils found
1395  // the vertical interpolation needs, which is as far up as this level is
1396  // guaranteed to cover it.
1397  // One box per location, in lev_locs order, so a gathered box and the
1398  // location it belongs to share an index. Two locations inside the same
1399  // cell give identical boxes, which is allowed: the BoxArray is only ever
1400  // a list of what to copy and what to index, never a cover of a region,
1401  // and ParallelCopy fills each of a pair of identical boxes from the same
1402  // source. The 2D boxes sit at k = 0 rather than at klo because ba2d is
1403  // built by compressing the 3D BoxArray with setRange(2,0), so 0 is where
1404  // its data is whatever the domain's smallEnd is (ERF_MakeNewArrays.cpp).
1405  BoxList bl_cc, bl_2d, bl_nd;
1406  for (const int ip : lev_locs[lev]) {
1407  const auto& loc = stations[all_locs[ip].first].locs[all_locs[ip].second];
1408  Box cc(IntVect(loc.i0,loc.j0,klo), IntVect(loc.i1,loc.j1,loc.ktop));
1409  bl_cc.push_back(cc);
1410  Box b2(IntVect(loc.i0,loc.j0,0), IntVect(loc.i1,loc.j1,0));
1411  bl_2d.push_back(b2);
1412  bl_nd.push_back(Box(IntVect(loc.i0,loc.j0,klo), IntVect(loc.i1+1,loc.j1+1,klo),
1413  IntVect::TheNodeVector()));
1414  }
1415 
1416  const int nbox = static_cast<int>(bl_cc.size());
1417  Vector<int> pmap(nbox, io_rank);
1418  DistributionMapping dm_io(pmap);
1419  MFInfo pinned = MFInfo().SetArena(The_Pinned_Arena());
1420 
1421  if (n3d > 0) {
1422  BoxArray ba(bl_cc);
1423  st3d[lev].define(ba, dm_io, n3d, 0, pinned);
1424  st3d[lev].setVal(Real(0.0));
1425  st3d[lev].ParallelCopy(mf3d[lev], 0, 0, n3d, IntVect(0), IntVect(0), period);
1426  }
1427  if (n2d > 0) {
1428  BoxArray ba(bl_2d);
1429  st2d[lev].define(ba, dm_io, n2d, 0, pinned);
1430  st2d[lev].setVal(Real(0.0));
1431  st2d[lev].ParallelCopy(mf2d[lev], 0, 0, n2d, IntVect(0), IntVect(0), period);
1432  }
1433  if (z_phys_cc[lev]) {
1434  BoxArray ba(bl_cc);
1435  stzc[lev].define(ba, dm_io, 1, 0, pinned);
1436  stzc[lev].setVal(Real(0.0));
1437  stzc[lev].ParallelCopy(*z_phys_cc[lev], 0, 0, 1, IntVect(0), IntVect(0), period);
1438  }
1439  if (z_phys_nd[lev]) {
1440  BoxArray ba(bl_nd);
1441  stznd[lev].define(ba, dm_io, 1, 0, pinned);
1442  stznd[lev].setVal(Real(0.0));
1443  stznd[lev].ParallelCopy(*z_phys_nd[lev], 0, 0, 1, IntVect(0), IntVect(0), period);
1444  }
1445  }
1446 
1447  // The gathers above are device-side in a GPU build, and what follows reads
1448  // the pinned destinations on the host.
1449  Gpu::streamSynchronize();
1450 
1451  // ---- interpolate, on the IO rank ----
1452  Vector<Real> row(ss.numColumns(), Real(0.0));
1453 
1454  if (ParallelDescriptor::IOProcessor())
1455  {
1456  for (int lev = 0; lev <= finest_level; ++lev)
1457  {
1458  if (lev_locs[lev].empty()) { continue; }
1459 
1460  const auto problo = geom[lev].ProbLoArray();
1461  const auto dx = geom[lev].CellSizeArray();
1462  const Box& dom = geom[lev].Domain();
1463  const int klo = dom.smallEnd(2);
1464 
1465  for (int ib = 0; ib < static_cast<int>(lev_locs[lev].size()); ++ib)
1466  {
1467  const int ip = lev_locs[lev][ib];
1468  const Station& station = stations[all_locs[ip].first];
1469  const StationLoc& loc = station.locs[all_locs[ip].second];
1470 
1471  const int i0 = loc.i0, j0 = loc.j0, i1 = loc.i1, j1 = loc.j1;
1472  const Real wx = loc.wx, wy = loc.wy;
1473 
1474  auto bilinear = [&](const Array4<const Real>& a, int comp, int k)
1475  {
1476  return (Real(1.0)-wy) * ( (Real(1.0)-wx)*a(i0,j0,k,comp) + wx*a(i1,j0,k,comp) )
1477  + wy * ( (Real(1.0)-wx)*a(i0,j1,k,comp) + wx*a(i1,j1,k,comp) );
1478  };
1479 
1480  // Height of the cell centres in the station's column
1481  Array4<const Real> zc_arr;
1482  if (z_phys_cc[lev]) { zc_arr = stzc[lev].const_array(ib); }
1483  auto z_of_k = [&](int k)
1484  {
1485  if (zc_arr) { return bilinear(zc_arr, 0, k); }
1486  return problo[2] + (Real(k-klo) + Real(0.5)) * dx[2];
1487  };
1488 
1489  // Terrain elevation under the station
1490  Real z_surf = problo[2];
1491  if (z_phys_nd[lev]) {
1492  const Array4<const Real>& nd = stznd[lev].const_array(ib);
1493  auto corner_avg = [&](int i, int j) {
1494  return Real(0.25) * (nd(i,j,klo) + nd(i+1,j,klo) + nd(i,j+1,klo) + nd(i+1,j+1,klo));
1495  };
1496  z_surf = (Real(1.0)-wy) * ( (Real(1.0)-wx)*corner_avg(i0,j0) + wx*corner_avg(i1,j0) )
1497  + wy * ( (Real(1.0)-wx)*corner_avg(i0,j1) + wx*corner_avg(i1,j1) );
1498  }
1499 
1500  Array4<const Real> a3, a2;
1501  if (n3d > 0) { a3 = st3d[lev].const_array(ib); }
1502  if (n2d > 0) { a2 = st2d[lev].const_array(ib); }
1503 
1504  // The columns of this location, in the order buildColumns assigned them
1505  int icol = loc.col_begin;
1506 
1507  for (const auto& var : station.vars) {
1508  if (!var.is_2d) { continue; }
1509  const int gcol = station.columns[icol++];
1510  row[gcol] = var.is_missing ? var.missing_value : bilinear(a2, var.comp, 0);
1511  }
1512 
1513  const Real z_zero = station.heights_are_agl ? z_surf : Real(0.0);
1514 
1515  for (const Real height : station.heights)
1516  {
1517  const Real z_target = z_zero + height;
1518 
1519  // Bracket the target height in the part of the column this
1520  // level was chosen to cover
1521  int kk = klo;
1522  while (kk < loc.ktop && z_of_k(kk+1) < z_target) { ++kk; }
1523  const Real zk = z_of_k(kk);
1524  const Real zk1 = z_of_k(std::min(kk+1, loc.ktop));
1525  Real wz = (zk1 > zk) ? (z_target - zk) / (zk1 - zk) : Real(0.0);
1526  wz = std::min(std::max(wz, Real(0.0)), Real(1.0)); // clamp below the first
1527  const int k1 = std::min(kk+1, loc.ktop); // cell centre and above the top
1528 
1529  for (const auto& var : station.vars) {
1530  if (var.is_2d) { continue; }
1531  const int gcol = station.columns[icol++];
1532  row[gcol] = var.is_missing
1533  ? var.missing_value
1534  : (Real(1.0)-wz)*bilinear(a3, var.comp, kk) + wz*bilinear(a3, var.comp, k1);
1535  }
1536  }
1537  }
1538  }
1539  }
1540 
1541  ss.appendRow(time, static_cast<Real>(start_time) + time, row);
1542 }
void BuildPlot3DScratch(const amrex::Vector< std::string > &plot_var_names, Plot3DScratch &scratch, int max_lev=-1, bool sync_solution=true)
Definition: ERF_Plotfile.cpp:298
void resolve_station_stencils()
Definition: ERF_StationSampler.cpp:968
void FillPlot2DVars(int lev, const amrex::Vector< std::string > &plot_var_names, const amrex::Vector< std::string > &varnames, const amrex::Vector< plotfile2d::Plotfile2DOutputDescriptor > &output_descriptors, amrex::MultiFab &mf_dst, int ncomp_mf)
Definition: ERF_Plotfile2D.cpp:166
void FillPlot3DVars(int lev, const amrex::Vector< std::string > &plot_var_names, Plot3DScratch &scratch, amrex::MultiFab &mf_dst, int ncomp_mf, amrex::Real tnew)
Definition: ERF_Plotfile.cpp:497
real(c_double), parameter a2
Definition: ERF_module_model_constants.F90:95
real(c_double), parameter a3
Definition: ERF_module_model_constants.F90:96
amrex::Vector< Plotfile2DOutputDescriptor > build_sampled_level_output_descriptors_from_definitions(const amrex::Vector< SampledLevelDefinition > &level_sets, const amrex::Vector< std::string > &static_plot_vars, const SolverChoice &solver_choice)
Definition: ERF_Plotfile2DSampledLevel.cpp:389
amrex::Real wx
Definition: ERF_StationSampler.H:110
int j1
Definition: ERF_StationSampler.H:109
amrex::Real wy
Definition: ERF_StationSampler.H:111
int i0
Definition: ERF_StationSampler.H:106
int col_begin
Definition: ERF_StationSampler.H:117
int i1
Definition: ERF_StationSampler.H:108
int j0
Definition: ERF_StationSampler.H:107
amrex::Vector< StationVar > vars
Definition: ERF_StationSampler.H:135
amrex::Vector< int > columns
Definition: ERF_StationSampler.H:145
Here is the call graph for this function:

◆ SampleLine()

amrex::IntVect& ERF::SampleLine ( int  i)
inlineprivate
2014  {
2015  return sampleline[i];
2016  }

◆ SampleLineLog()

AMREX_FORCE_INLINE std::ostream& ERF::SampleLineLog ( int  i)
inlineprivate
1988  {
1989  return *samplelinelog[i];
1990  }

◆ SampleLineLogName()

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

The filename of the ith samplelinelog file.

2156 { return samplelinelogname[i]; }

◆ SamplePoint()

amrex::IntVect& ERF::SamplePoint ( int  i)
inlineprivate
2001  {
2002  return samplepoint[i];
2003  }

◆ SamplePointLog()

AMREX_FORCE_INLINE std::ostream& ERF::SamplePointLog ( int  i)
inlineprivate
1974  {
1975  return *sampleptlog[i];
1976  }

◆ SamplePointLogName()

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

The filename of the ith sampleptlog file.

2153 { return sampleptlogname[i]; }

◆ SetDirsForPlotfilesAndCheckpointsForDA()

void ERF::SetDirsForPlotfilesAndCheckpointsForDA ( const int  da_iter,
const int  ens_no 
)

◆ SetDriverAtmosToOceanStateContract()

void ERF::SetDriverAtmosToOceanStateContract ( bool  use_state_contract)
258 {
259  m_driver_atm2ocn_uses_state_contract = use_state_contract;
260 }

◆ setPlotVariables()

void ERF::setPlotVariables ( const std::string &  pp_plot_var_names,
amrex::Vector< std::string > &  plot_var_names 
)
private
20 {
21  ParmParse pp(pp_prefix);
22 
23  if (pp.contains(pp_plot_var_names.c_str()))
24  {
25  std::string nm;
26 
27  int nPltVars = pp.countval(pp_plot_var_names.c_str());
28 
29  for (int i = 0; i < nPltVars; i++)
30  {
31  pp.get(pp_plot_var_names.c_str(), nm, i);
32 
33  // Add the named variable to our list of plot variables
34  // if it is not already in the list
35  if (!containerHasElement(plot_var_names, nm)) {
36  plot_var_names.push_back(nm);
37  }
38  }
39  } else {
40  //
41  // The default is to add none of the variables to the list
42  //
43  plot_var_names.clear();
44  }
45 
46  canonicalizePlot3DVariables(plot_var_names);
47 }

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
114 {
115  ParmParse pp(pp_prefix);
116 
117  if (!pp.contains(pp_plot_var_names.c_str())) {
118  //
119  // The default is to add none of the variables to the list
120  //
121  plot_var_names.clear();
122  return;
123  }
124 
125  Vector<std::string> requested_plot_names;
126  std::string nm;
127  const int nPltVars = pp.countval(pp_plot_var_names.c_str());
128  for (int i = 0; i < nPltVars; ++i) {
129  pp.get(pp_plot_var_names.c_str(), nm, i);
130  requested_plot_names.push_back(nm);
131  }
132 
133  const bool has_surface_layer =
134  phys_bc_type[Orientation(Direction::z, Orientation::low)] == ERF_BC::surface_layer;
135  const auto active_lsm_names = lsm.Get_DataNames();
136  const auto available_names = plotfile2d::available_diagnostic_names(solverChoice,
137  has_surface_layer,
138  active_lsm_names);
139 
140  // Keep the canonical built-in 2D ordering so the plotfile component layout
141  // stays stable even if the input request order changes.
142  const auto selection = plotfile2d::select_requested_plot_variables(requested_plot_names,
143  available_names);
144  plot_var_names = selection.accepted;
145 
146  // Unknown 2D names are skipped rather than aborting because the 2D plot
147  // list is intentionally user-configurable and may include names that are not
148  // compiled into a given build. The warning is still explicit so the user
149  // can correct the input deck.
150  warn_for_unavailable_2d_plot_vars(plotfile2d::format_plot2d_parameter_name(pp_prefix, pp_plot_var_names),
151  selection.unavailable, available_names);
152 }
std::string format_plot2d_parameter_name(const std::string &pp_prefix, const std::string &parameter_name)
Definition: ERF_Plotfile2DUtils.cpp:78
Here is the call 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.
86 {
87  // If we are restarting then we haven't read the input_sounding file yet
88  // so we need to read it here
89  // TODO: should we store this information in the checkpoint file instead?
90  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
91  if (restarting) {
93  for (int n = 0; n < input_sounding_data.n_sounding_files; n++) {
94  input_sounding_data.read_from_file(geom[0], zlevels_stag[0], n, is_moist);
95  }
96  }
97 
98  const Real* z_inp_sound = input_sounding_data.z_inp_sound[0].dataPtr();
99  const Real* U_inp_sound = input_sounding_data.U_inp_sound[0].dataPtr();
100  const Real* V_inp_sound = input_sounding_data.V_inp_sound[0].dataPtr();
101  const Real* theta_inp_sound = input_sounding_data.theta_inp_sound[0].dataPtr();
102  const int inp_sound_size = input_sounding_data.size(0);
103 
104  int refine_fac{1};
105  for (int lev = 0; lev <= finest_level; lev++)
106  {
107  const int klo = geom[lev].Domain().smallEnd(2);
108  const int khi = geom[lev].Domain().bigEnd(2);
109  const int Nz = khi - klo + 1;
110 
111  Vector<Real> zcc(Nz);
112  Vector<Real> zlevels_sub(zlevels_stag[0].begin()+klo/refine_fac,
113  zlevels_stag[0].begin()+khi/refine_fac+2);
114  expand_and_interpolate_1d(zcc, zlevels_sub, refine_fac, true);
115 #if 0
116  amrex::AllPrint() << "lev="<<lev<<" : (refine_fac="<<refine_fac<<",klo="<<klo<<",khi="<<khi<<") ";
117  for (int k = 0; k < zlevels_sub.size(); k++) { amrex::AllPrint() << zlevels_sub[k] << " "; }
118  amrex::AllPrint() << " --> ";
119  for (int k = 0; k < Nz; k++) { amrex::AllPrint() << zcc[k] << " "; }
120  amrex::AllPrint() << std::endl;
121 #endif
122 
123  for (int k = 0; k < Nz; k++)
124  {
125  h_rayleigh_ptrs[lev][Rayleigh::ubar][k] = interpolate_1d(z_inp_sound, U_inp_sound, zcc[k], inp_sound_size);
126  h_rayleigh_ptrs[lev][Rayleigh::vbar][k] = interpolate_1d(z_inp_sound, V_inp_sound, zcc[k], inp_sound_size);
128  h_rayleigh_ptrs[lev][Rayleigh::thetabar][k] = interpolate_1d(z_inp_sound, theta_inp_sound, zcc[k], inp_sound_size);
129  }
130 
131  // Copy from host version to device version
132  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::ubar].begin(), h_rayleigh_ptrs[lev][Rayleigh::ubar].end(),
133  d_rayleigh_ptrs[lev][Rayleigh::ubar].begin());
134  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::vbar].begin(), h_rayleigh_ptrs[lev][Rayleigh::vbar].end(),
135  d_rayleigh_ptrs[lev][Rayleigh::vbar].begin());
136  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::wbar].begin(), h_rayleigh_ptrs[lev][Rayleigh::wbar].end(),
137  d_rayleigh_ptrs[lev][Rayleigh::wbar].begin());
138  Gpu::copy(Gpu::hostToDevice, h_rayleigh_ptrs[lev][Rayleigh::thetabar].begin(), h_rayleigh_ptrs[lev][Rayleigh::thetabar].end(),
139  d_rayleigh_ptrs[lev][Rayleigh::thetabar].begin());
140 
141  if (lev < finest_level) {
142  refine_fac *= ref_ratio[lev][2];
143  }
144  }
145 }
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 > > V_inp_sound
Interpolated host y velocity profiles.
Definition: ERF_InputSoundingData.H:506
amrex::Vector< amrex::Vector< amrex::Real > > theta_inp_sound
Interpolated host potential temperature profiles [K].
Definition: ERF_InputSoundingData.H:506
amrex::Vector< amrex::Vector< amrex::Real > > U_inp_sound
Interpolated host x velocity profiles.
Definition: ERF_InputSoundingData.H:506
amrex::Vector< amrex::Vector< amrex::Real > > z_inp_sound
Interpolated host sounding heights for each sounding time [m].
Definition: ERF_InputSoundingData.H:506
int size(int itime) const
Return the number of interpolated samples for one sounding time.
Definition: ERF_InputSoundingData.H:473
Here is the call graph for this function:

◆ setRecordDataInfo()

void ERF::setRecordDataInfo ( int  i,
const std::string &  filename 
)
inlineprivate
2044  {
2045  if (amrex::ParallelDescriptor::IOProcessor())
2046  {
2047  datalog[i] = std::make_unique<std::fstream>();
2048  datalog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
2049  if (!datalog[i]->good()) {
2050  amrex::FileOpenFailed(filename);
2051  }
2052  }
2053  amrex::ParallelDescriptor::Barrier("ERF::setRecordDataInfo");
2054  }

◆ setRecordDerDataInfo()

void ERF::setRecordDerDataInfo ( int  i,
const std::string &  filename 
)
inlineprivate
2057  {
2058  if (amrex::ParallelDescriptor::IOProcessor())
2059  {
2060  der_datalog[i] = std::make_unique<std::fstream>();
2061  der_datalog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
2062  if (!der_datalog[i]->good()) {
2063  amrex::FileOpenFailed(filename);
2064  }
2065  }
2066  amrex::ParallelDescriptor::Barrier("ERF::setRecordDerDataInfo");
2067  }

◆ setRecordEnergyDataInfo()

void ERF::setRecordEnergyDataInfo ( int  i,
const std::string &  filename 
)
inlineprivate
2070  {
2071  if (amrex::ParallelDescriptor::IOProcessor())
2072  {
2073  tot_e_datalog[i] = std::make_unique<std::fstream>();
2074  tot_e_datalog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
2075  if (!tot_e_datalog[i]->good()) {
2076  amrex::FileOpenFailed(filename);
2077  }
2078  }
2079  amrex::ParallelDescriptor::Barrier("ERF::setRecordEnergyDataInfo");
2080  }

◆ setRecordSampleLineInfo()

void ERF::setRecordSampleLineInfo ( int  i,
int  lev,
amrex::IntVect &  cell,
const std::string &  filename 
)
inlineprivate
2100  {
2101  amrex::MultiFab dummy(grids[lev],dmap[lev],1,0);
2102  for (amrex::MFIter mfi(dummy); mfi.isValid(); ++mfi)
2103  {
2104  const amrex::Box& bx = mfi.validbox();
2105  if (bx.contains(cell)) {
2106  samplelinelog[i] = std::make_unique<std::fstream>();
2107  samplelinelog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
2108  if (!samplelinelog[i]->good()) {
2109  amrex::FileOpenFailed(filename);
2110  }
2111  }
2112  }
2113  amrex::ParallelDescriptor::Barrier("ERF::setRecordSampleLineInfo");
2114  }

◆ setRecordSamplePointInfo()

void ERF::setRecordSamplePointInfo ( int  i,
int  lev,
amrex::IntVect &  cell,
const std::string &  filename 
)
inlineprivate
2083  {
2084  amrex::MultiFab dummy(grids[lev],dmap[lev],1,0);
2085  for (amrex::MFIter mfi(dummy); mfi.isValid(); ++mfi)
2086  {
2087  const amrex::Box& bx = mfi.validbox();
2088  if (bx.contains(cell)) {
2089  sampleptlog[i] = std::make_unique<std::fstream>();
2090  sampleptlog[i]->open(filename.c_str(),std::ios::out|std::ios::app);
2091  if (!sampleptlog[i]->good()) {
2092  amrex::FileOpenFailed(filename);
2093  }
2094  }
2095  }
2096  amrex::ParallelDescriptor::Barrier("ERF::setRecordSamplePointInfo");
2097  }

◆ 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 > z_inp_sponge
Interpolated sponge profile heights [m].
Definition: ERF_InputSpongeData.H:123
amrex::Vector< amrex::Real > V_inp_sponge
Interpolated target y velocity for sponge damping.
Definition: ERF_InputSpongeData.H:123
amrex::Vector< amrex::Real > U_inp_sponge
Interpolated target x velocity for sponge damping.
Definition: ERF_InputSpongeData.H:123
int size() const
Return the number of interpolated input sponge samples.
Definition: ERF_InputSpongeData.H:111
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
15 {
16  ParmParse pp(pp_prefix);
17 
18  std::string nm;
19 
20  int nSubVolVars = pp.countval(pp_subvol_var_names.c_str());
21 
22  // We pre-populate the list with velocities, but allow these to be over-written
23  // by user input
24  if (nSubVolVars == 0)
25  {
26  subvol_var_names.push_back("x_velocity");
27  subvol_var_names.push_back("y_velocity");
28  subvol_var_names.push_back("z_velocity");
29 
30  } else {
31  for (int i = 0; i < nSubVolVars; i++)
32  {
33  pp.get(pp_subvol_var_names.c_str(), nm, i);
34 
35  // Add the named variable to our list of subvol variables
36  // if it is not already in the list
37  if (!containerHasElement(subvol_var_names, nm)) {
38  subvol_var_names.push_back(nm);
39  }
40  }
41  }
42 
43  // Get state variables in the same order as we define them,
44  // since they may be in any order in the input list
45  Vector<std::string> tmp_plot_names;
46 
47  // NOTE: raw state names are selected with exactly the same predicate the 3D
48  // plotfile uses, so the two output paths cannot drift. It answers from
49  // the allocated width for the dry components and for the non-water
50  // species some schemes carry above the moist window, and from the
51  // moisture map inside that window -- a "rhoQn" the model does not carry
52  // would otherwise be read out of bounds when we ParallelCopy it below.
54 
57  mi,
58  micro->Get_Qstate_Moist_Size(),
59  micro->Get_Qstate_Size());
60 
61  for (int i = 0; i < cons_names.size(); ++i) {
62  if ( containerHasElement(subvol_var_names, cons_names[i]) ) {
64  tmp_plot_names.push_back(cons_names[i]);
65  }
66  }
67  }
68 
69  // Check for velocity since it's not in cons_names
70  if (containerHasElement(subvol_var_names, "x_velocity")) {
71  tmp_plot_names.push_back("x_velocity");
72  }
73  if (containerHasElement(subvol_var_names, "y_velocity")) {
74  tmp_plot_names.push_back("y_velocity");
75  }
76  if (containerHasElement(subvol_var_names, "z_velocity")) {
77  tmp_plot_names.push_back("z_velocity");
78  }
79 
80  //
81  // If the model we are running doesn't have the variable listed in the inputs file,
82  // just ignore it rather than aborting
83  //
84  // NOTE: we walk "derived_names" (not "derived_subvol_names") because the names must be
85  // pushed in the order WriteSubvolume computes them, and that order is the
86  // "derived_names" order. "derived_subvol_names" is the unordered allow-list of
87  // the derived quantities WriteSubvolume knows how to fill.
88  //
89  // NOTE: there are no terrain-dependent names to gate here -- "z_phys", "detJ" and
90  // "terrain_IB_mask" are not in "derived_subvol_names", so WriteSubvolume
91  // cannot fill them and they never reach this loop.
92  //
93  for (int i = 0; i < derived_names.size(); ++i) {
94  if ( containerHasElement(subvol_var_names, derived_names[i]) &&
96  // NOTE: WriteSubvolume only computes "mucape" for a moist run, so it must not be
97  // named for a dry one even though the kernel itself is dry-safe. The
98  // moisture map does not govern that name -- the 3D plotfile writes it for
99  // dry runs too -- so it is gated here rather than in has_derived_var().
100  const bool ok_to_add = ( mi.has_moisture() || (derived_names[i] != "mucape") );
101  if (ok_to_add && mi.has_derived_var(derived_names[i]))
102  {
103  tmp_plot_names.push_back(derived_names[i]);
104  }
105  } // hasElement
106  }
107 
108  subvol_var_names = tmp_plot_names;
109 }
const amrex::Vector< std::string > derived_subvol_names
Definition: ERF.H:1642
bool has_derived_var(const std::string &name) const
Test whether the moist species behind a derived/plot variable name is carried by the active scheme.
Definition: ERF_DataStruct.H:652

Referenced by ERF_shared().

Here is the call graph for this function:
Here is the caller 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

Parameters
levLevel index for the solve
subdomainBox over which the solve is performed
rhsRight-hand side field for the Poisson solve
pSolution field to fill
fluxesFace-centered gradient fluxes to fill
ax_subTerrain metric coefficient on x-faces
ay_subTerrain metric coefficient on y-faces
az_subTerrain metric coefficient on z-faces
znd_subNode-centered physical height field

The unnamed metric argument in the class declaration is the cell-centered Jacobian determinant.

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

◆ sum_derived_quantities()

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

◆ sum_energy_quantities()

void ERF::sum_energy_quantities ( double  time)
319 {
320  if ( (verbose <= 0) || (tot_e_datalog.size() < 1) ) { return; }
321 
322  int lev = 0;
323 
324  auto& mfx0 = *mapfac[0][MapFacType::m_x];
325  auto& mfy0 = *mapfac[0][MapFacType::m_x];
326  auto& dJ0 = *detJ_cc[0];
327 
328  AMREX_ALWAYS_ASSERT(lev == 0);
329 
330  bool local = true;
331 
332  // ************************************************************************
333  // WARNING: we are not filling ghost cells other than periodic outside the domain
334  // ************************************************************************
335 
336  MultiFab mf_cc_vel(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,1));
337  mf_cc_vel.setVal(0.); // We just do this to avoid uninitialized values
338 
339  // Average all three components of velocity (on faces) to the cell center
340  average_face_to_cellcenter(mf_cc_vel,0,
341  Array<const MultiFab*,3>{&vars_new[lev][Vars::xvel],
342  &vars_new[lev][Vars::yvel],
343  &vars_new[lev][Vars::zvel]});
344  mf_cc_vel.FillBoundary(geom[lev].periodicity());
345 
346  if (!geom[lev].isPeriodic(0) || !geom[lev].isPeriodic(1) || !geom[lev].isPeriodic(2)) {
347  amrex::Warning("Ghost cells outside non-periodic physical boundaries are not filled -- vel set to 0 there");
348  }
349 
350  MultiFab tot_mass (grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
351  MultiFab tot_energy(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(0,0,0));
352 
353  auto const& dx = geom[lev].CellSizeArray();
354  bool is_moist = (solverChoice.moisture_type != MoistureType::None);
355 
356 #ifdef _OPENMP
357 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
358 #endif
359  for (MFIter mfi(tot_mass, TilingIfNotGPU()); mfi.isValid(); ++mfi)
360  {
361  const Box& bx = mfi.tilebox();
362 
363  const Array4<Real>& cc_vel_arr = mf_cc_vel.array(mfi);
364  const Array4<Real>& tot_mass_arr = tot_mass.array(mfi);
365  const Array4<Real>& tot_energy_arr = tot_energy.array(mfi);
366  const Array4<const Real>& cons_arr = vars_new[lev][Vars::cons].const_array(mfi);
367  const Array4<const Real>& z_arr = (z_phys_nd[lev]) ? z_phys_nd[lev]->const_array(mfi) :
368  Array4<const Real>{};
369  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
370  {
371  Real Qv = (is_moist) ? cons_arr(i,j,k,RhoQ1_comp) : zero;
372  Real Qc = (is_moist) ? cons_arr(i,j,k,RhoQ2_comp) : zero;
373  Real Qt = Qv + Qc;
374  Real Rhod = cons_arr(i,j,k,Rho_comp);
375  Real Rhot = Rhod * (one + Qt);
376  Real Temp = getTgivenRandRTh(Rhod, cons_arr(i,j,k,RhoTheta_comp), Qv);
377  Real TKE = myhalf * ( cc_vel_arr(i,j,k,0)*cc_vel_arr(i,j,k,0)
378  + cc_vel_arr(i,j,k,1)*cc_vel_arr(i,j,k,1)
379  + cc_vel_arr(i,j,k,2)*cc_vel_arr(i,j,k,2) );
380  Real zval = (z_arr) ? z_arr(i,j,k) : Real(k)*dx[2];
381 
382  Real Cv = Cp_d - R_d;
383  Real Cvv = Cp_v - R_v;
384  Real Cpv = Cp_v;
385 
386  tot_mass_arr(i,j,k) = Rhot;
387  tot_energy_arr(i,j,k) = Rhod * ( (Cv + Cvv*Qv + Cpv*Qc)*Temp - L_v*Qc
388  + (one + Qt)*TKE + (one + Qt)*CONST_GRAV*zval );
389 
390  });
391 
392  }
393 
394  Real tot_mass_avg = volWgtSumMF(lev, tot_mass , 0, dJ0, mfx0, mfy0, false, local);
395  Real tot_energy_avg = volWgtSumMF(lev, tot_energy, 0, dJ0, mfx0, mfy0, false, local);
396 
397  // Get volume including terrain (consistent with volWgtSumMF routine)
398  MultiFab volume(grids[lev], dmap[lev], 1, 0);
399  Real cell_vol = dx[0]*dx[1]*dx[2];
400  volume.setVal(cell_vol);
401  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
402  MultiFab::Multiply(volume, *detJ_cc[lev], 0, 0, 1, 0);
403  }
404 #ifdef _OPENMP
405 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
406 #endif
407  for (MFIter mfi(volume, TilingIfNotGPU()); mfi.isValid(); ++mfi)
408  {
409  const Box& tbx = mfi.tilebox();
410  auto dst = volume.array(mfi);
411  const auto& mfx = mapfac[lev][MapFacType::m_x]->const_array(mfi);
412  const auto& mfy = mapfac[lev][MapFacType::m_y]->const_array(mfi);
413  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
414  {
415  dst(i,j,k) /= (mfx(i,j,0)*mfy(i,j,0));
416  });
417  }
418  Real vol = volume.sum();
419 
420  // Divide by the volume
421  tot_mass_avg /= vol;
422  tot_energy_avg /= vol;
423 
424  const int nfoo = 2;
425  Real foo[nfoo] = {tot_mass_avg,tot_energy_avg};
426  Real zero_d = zero;
427 #ifdef AMREX_LAZY
428  Lazy::QueueReduction([=]() mutable {
429 #endif
430  ParallelDescriptor::ReduceRealSum(
431  foo, nfoo, ParallelDescriptor::IOProcessorNumber());
432 
433  if (ParallelDescriptor::IOProcessor()) {
434  int i = 0;
435  tot_mass_avg = foo[i++];
436  tot_energy_avg = foo[i++];
437 
438  std::ostream& data_log_energy = *tot_e_datalog[0];
439 
440  if (time == zero_d) {
441  data_log_energy << std::setw(datwidth) << " time";
442  data_log_energy << std::setw(datwidth) << " tot_mass";
443  data_log_energy << std::setw(datwidth) << " tot_energy";
444  data_log_energy << std::endl;
445  }
446  data_log_energy << std::setw(datwidth) << std::setprecision(timeprecision) << time;
447  data_log_energy << std::setw(datwidth) << std::setprecision(datprecision) << tot_mass_avg;
448  data_log_energy << std::setw(datwidth) << std::setprecision(datprecision) << tot_energy_avg;
449  data_log_energy << std::endl;
450 
451  } // if IOProcessor
452 #ifdef AMREX_LAZY
453  });
454 #endif
455 }
constexpr amrex::Real R_v
Definition: ERF_Constants.H:35
constexpr amrex::Real Cp_v
Definition: ERF_Constants.H:37
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
20 {
21  BL_PROFILE("ERF::sum_integrated_quantities()");
22 
23  if (verbose <= 0)
24  return;
25 
26  // Single level sum
27  Real mass_sl;
28 
29  // Multilevel sums
30  Real mass_ml = zero;
31  Real rhth_ml = zero;
32  Real scal_ml = zero;
33  Real mois_ml = zero;
34 
35  bool local = true;
36 
37  auto& mfx0 = *mapfac[0][MapFacType::m_x];
38  auto& mfy0 = *mapfac[0][MapFacType::m_x];
39  auto& dJ0 = *detJ_cc[0];
40 
41  mass_sl = volWgtSumMF(0,vars_new[0][Vars::cons],Rho_comp,dJ0,mfx0,mfy0,false,local);
42 
43  for (int lev = 0; lev <= finest_level; lev++) {
44  auto& mfx = *mapfac[lev][MapFacType::m_x];
45  auto& mfy = *mapfac[lev][MapFacType::m_x];
46  auto& dJ = *detJ_cc[lev];
47  mass_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons],Rho_comp,dJ,mfx,mfy,true);
48  }
49 
50  Real rhth_sl = volWgtSumMF(0,vars_new[0][Vars::cons], RhoTheta_comp,dJ0,mfx0,mfy0,false);
51  Real scal_sl = volWgtSumMF(0,vars_new[0][Vars::cons],RhoScalar_comp,dJ0,mfx0,mfy0,false);
52  Real mois_sl = zero;
53  if (solverChoice.moisture_type != MoistureType::None) {
54  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
55  for (int qoff(0); qoff<n_qstate_into_total; ++qoff) {
56  mois_sl += volWgtSumMF(0,vars_new[0][Vars::cons],RhoQ1_comp+qoff,dJ0,mfx0,mfy0,false);
57  }
58  }
59 
60  for (int lev = 0; lev <= finest_level; lev++) {
61  auto& mfx = *mapfac[lev][MapFacType::m_x];
62  auto& mfy = *mapfac[lev][MapFacType::m_x];
63  auto& dJ = *detJ_cc[lev];
64  rhth_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons], RhoTheta_comp,dJ,mfx,mfy,true);
65  scal_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons],RhoScalar_comp,dJ,mfx,mfy,true);
66  if (solverChoice.moisture_type != MoistureType::None) {
67  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
68  for (int qoff(0); qoff<n_qstate_into_total; ++qoff) {
69  mois_ml += volWgtSumMF(lev,vars_new[lev][Vars::cons],RhoQ1_comp+qoff,dJ,mfx,mfy,true);
70  }
71  }
72  }
73 
74  Real h_avg_ustar = zero;
75  Real h_avg_tstar = zero;
76  Real h_avg_olen = zero;
77  Orientation zlo(Direction::z, Orientation::low);
78  if ((m_SurfaceLayer[zlo] != nullptr) && (NumDataLogs() > 0)) {
79  // Sum each surface cell once: on grids split in z the planar arrays hold one
80  // duplicate box per stacked 3D box
81  h_avg_ustar = m_SurfaceLayer[zlo]->surface_sum(0, *m_SurfaceLayer[zlo]->get_u_star(0));
82  h_avg_tstar = m_SurfaceLayer[zlo]->surface_sum(0, *m_SurfaceLayer[zlo]->get_t_star(0));
83  h_avg_olen = m_SurfaceLayer[zlo]->surface_sum(0, *m_SurfaceLayer[zlo]->get_olen(0));
84 
85  // Divide by the total number of cells we are averaging over
86  const Box& domain = geom[0].Domain();
87  const Real area_z = static_cast<Real>(domain.length(0)*domain.length(1));
88  h_avg_ustar /= area_z;
89  h_avg_tstar /= area_z;
90  h_avg_olen /= area_z;
91  }
92 
93  const int nfoo = 8;
94  Real foo[nfoo] = {mass_sl,rhth_sl,scal_sl,mois_sl,mass_ml,rhth_ml,scal_ml,mois_ml};
95  Real zero_d = zero;
96 #ifdef AMREX_LAZY
97  Lazy::QueueReduction([=]() mutable {
98 #endif
99  ParallelDescriptor::ReduceRealSum(
100  foo, nfoo, ParallelDescriptor::IOProcessorNumber());
101 
102  if (ParallelDescriptor::IOProcessor()) {
103  int i = 0;
104  mass_sl = foo[i++];
105  rhth_sl = foo[i++];
106  scal_sl = foo[i++];
107  mois_sl = foo[i++];
108  mass_ml = foo[i++];
109  rhth_ml = foo[i++];
110  scal_ml = foo[i++];
111  mois_ml = foo[i++];
112 
113  Print() << '\n';
114  Print() << "TIME= " << std::setw(datwidth) << std::setprecision(timeprecision) << std::left << time << '\n';
115  if (finest_level == 0) {
116 #if 1
117  Print() << " MASS = " << mass_sl << '\n';
118 #else
119  Print() << " PERT MASS = " << mass_sl << '\n';
120 #endif
121  Print() << " RHO THETA = " << rhth_sl << '\n';
122  if (solverChoice.transport_scalar) { Print() << " RHO SCALAR = " << scal_sl << '\n'; }
123  if (solverChoice.moisture_type != MoistureType::None) { Print() << " RHO QTOTAL = " << mois_sl << '\n'; }
124  } else {
125 #if 1
126  Print() << " MASS SL/ML = " << mass_sl << " " << mass_ml << '\n';
127 #else
128  Print() << " PERT MASS SL/ML = " << mass_sl << " " << mass_ml << '\n';
129 #endif
130  Print() << " RHO THETA SL/ML = " << rhth_sl << " " << rhth_ml << '\n';
131  if (solverChoice.transport_scalar) { Print() << " RHO SCALAR SL/ML = " << scal_sl << " " << scal_ml << '\n'; }
132  if (solverChoice.moisture_type != MoistureType::None) { Print() << " RHO QTOTAL SL/ML = " << mois_sl << " " << mois_ml << '\n'; }
133  }
134 
135  // The first data log only holds scalars
136  if (NumDataLogs() > 0)
137  {
138  int n_d = 0;
139  std::ostream& data_log1 = DataLog(n_d);
140  if (data_log1.good()) {
141  if (time == zero_d) {
142  data_log1 << std::setw(datwidth) << " time";
143  data_log1 << std::setw(datwidth) << " u_star";
144  data_log1 << std::setw(datwidth) << " t_star";
145  data_log1 << std::setw(datwidth) << " olen";
146  data_log1 << std::endl;
147  } // time = 0
148 
149  // Write the quantities at this time
150  data_log1 << std::setw(datwidth) << std::setprecision(timeprecision) << time;
151  data_log1 << std::setw(datwidth) << std::setprecision(datprecision) << h_avg_ustar;
152  data_log1 << std::setw(datwidth) << std::setprecision(datprecision) << h_avg_tstar;
153  data_log1 << std::setw(datwidth) << std::setprecision(datprecision) << h_avg_olen;
154  data_log1 << std::endl;
155  } // if good
156  } // loop over i
157  } // if IOProcessor
158 #ifdef AMREX_LAZY
159  });
160 #endif
161 
162  // This is just an alias for convenience
163  int lev = 0;
164  if (NumSamplePointLogs() > 0 && NumSamplePoints() > 0) {
165  for (int i = 0; i < NumSamplePoints(); ++i)
166  {
167  sample_points(lev, time, SamplePoint(i), vars_new[lev][Vars::cons]);
168  }
169  }
170  if (NumSampleLineLogs() > 0 && NumSampleLines() > 0) {
171  for (int i = 0; i < NumSampleLines(); ++i)
172  {
173  sample_lines(lev, time, SampleLine(i), vars_new[lev][Vars::cons]);
174  }
175  }
176 }
AMREX_FORCE_INLINE int NumSampleLineLogs() noexcept
Definition: ERF.H:1994
AMREX_FORCE_INLINE int NumSamplePointLogs() noexcept
Definition: ERF.H:1980
amrex::IntVect & SampleLine(int i)
Definition: ERF.H:2013
void sample_points(int lev, double time, amrex::IntVect cell, amrex::MultiFab &mf)
Definition: ERF_WriteScalarProfiles.cpp:537
AMREX_FORCE_INLINE int NumSamplePoints() noexcept
Definition: ERF.H:2007
void sample_lines(int lev, double time, amrex::IntVect cell, amrex::MultiFab &mf)
Definition: ERF_WriteScalarProfiles.cpp:573
AMREX_FORCE_INLINE int NumSampleLines() noexcept
Definition: ERF.H:2020
amrex::IntVect & SamplePoint(int i)
Definition: ERF.H:2000
AMREX_FORCE_INLINE std::ostream & DataLog(int i)
Definition: ERF.H:1944
AMREX_FORCE_INLINE int NumDataLogs() noexcept
Definition: ERF.H:1958
bool transport_scalar
Whether the passive scalar component is transported.
Definition: ERF_DataStruct.H:2116

◆ SurfaceDataInterpolation()

void ERF::SurfaceDataInterpolation ( const int  lev,
const double  time,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  a_z_phys_nd,
bool  regrid_forces_file_read 
)

Interpolate weather forecast surface data in time and space onto the simulation grid.

Parameters
[in]levCurrent level.
[in]timeCurrent simulation time.
[in]a_z_phys_ndPhysical height MultiFabs.
[in]regrid_forces_file_readFlag indicating if a regrid requires a file read.
161 {
162 
163  static amrex::Vector<double> next_read_forecast_time;
164  static amrex::Vector<double> last_read_forecast_time;
165 
166  const int nlevs = a_z_phys_nd.size();
167 
168  Real hindcast_data_interval = solverChoice.hindcast_data_interval_in_hrs*Real(3600.0);
169 
170  // Initialize static vectors once
171  if (next_read_forecast_time.empty()) {
172  next_read_forecast_time.resize(nlevs, -one);
173  last_read_forecast_time.resize(nlevs, -one);
174  Print() << "Initializing the time vector values here by " << lev << std::endl;
175  }
176 
177  if (next_read_forecast_time[lev] < zero) {
178  int next_multiple = static_cast<int>(time / hindcast_data_interval);
179  next_read_forecast_time[lev] = next_multiple * hindcast_data_interval;
180  last_read_forecast_time[lev] = next_read_forecast_time[lev];
181  }
182 
183  if (time >= next_read_forecast_time[lev] or regrid_forces_file_read) {
184 
185  Print() << "Data reading happening at level " << lev << std::endl;
186 
187  std::string folder = solverChoice.hindcast_surface_data_dir;
188 
189  // Check if folder exists and is a directory
190  if (!fs::exists(folder) || !fs::is_directory(folder)) {
191  throw std::runtime_error("Error: Folder '" + folder + "' does not exist or is not a directory.");
192  }
193 
194  std::vector<std::string> bin_files;
195 
196  for (const auto& entry : fs::directory_iterator(folder)) {
197  if (!entry.is_regular_file()) continue;
198 
199  std::string fname = entry.path().filename().string();
200  if (fname.size() >= 4 && fname.substr(fname.size() - 4) == ".bin") {
201  bin_files.push_back(entry.path().string());
202  }
203  }
204  std::sort(bin_files.begin(), bin_files.end());
205 
206  // Check if no .bin files were found
207  if (bin_files.empty()) {
208  throw std::runtime_error("Error: No .bin files found in folder '" + folder + "'.");
209  }
210 
211  std::string filename1, filename2;
212 
213  int idx1 = static_cast<int>(time / hindcast_data_interval);
214  int idx2 = static_cast<int>(time / hindcast_data_interval)+1;
215  Print() << "Reading surface data " << time << " " << idx1 << " " << idx2 <<" " << bin_files.size() << std::endl;
216 
217  if (idx2 >= static_cast<int>(bin_files.size())) {
218  throw std::runtime_error("Error: Not enough .bin files to cover time " + std::to_string(time));
219  }
220 
221  filename1 = bin_files[idx1];
222  filename2 = bin_files[idx2];
223 
226 
227  // Create the time-interpolated forecast state
228  //CreateForecastStateMultiFabs(forecast_state_interp);
229  if(!regrid_forces_file_read){
230  last_read_forecast_time[lev] = next_read_forecast_time[lev];
231  next_read_forecast_time[lev] += hindcast_data_interval;
232  Print() << "Next forecast time getting updated here " << std::endl;
233  }
234  }
235 
236  double prev_read_time = last_read_forecast_time[lev];
237  Real alpha1 = static_cast<Real>(1.0 - (time - prev_read_time)/hindcast_data_interval);
238  Real alpha2 = one - alpha1;
239 
240  amrex::Print()<< "The values of alpha1 and alpha2 are " << alpha1 << " "<< alpha2 <<std::endl;
241 
242  if (alpha1 < zero || alpha1 > one ||
243  alpha2 < zero || alpha2 > one)
244  {
245  std::stringstream ss;
246  ss << "Interpolation weights for hindcast files are incorrect: "
247  << "alpha1 = " << alpha1 << ", alpha2 = " << alpha2;
248  Abort(ss.str());
249  }
250 
251  MultiFab& mf_surf_interp = surface_state_interp[lev];
252 
253  // Fill the time-interpolated forecast states
254  MultiFab::LinComb(surface_state_interp[lev],
255  alpha1, surface_state_1[lev], 0,
256  alpha2, surface_state_2[lev], 0,
257  0, mf_surf_interp.nComp(), mf_surf_interp.nGrow());
258 
259  /* debug plotfile dump follows — leave commented out
260  std::string pltname = "plt_interp_surface";
261  Vector<std::string> varnames_plot_mf = {"ls_mask", "SST"};
262 
263  const MultiFab& src = vars_new[0][0];
264 
265  MultiFab plot_mf(src.boxArray(),
266  src.DistributionMap(),
267  2, 0);
268 
269  plot_mf.setVal(0.0);
270 
271  for (MFIter mfi(plot_mf); mfi.isValid(); ++mfi) {
272  const Array4<Real> &plot_mf_arr = plot_mf.array(mfi);
273  const Array4<Real> &surf_mf_arr = surface_state_1[0].array(mfi);
274 
275  const Box& bx = mfi.validbox();
276 
277  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
278  plot_mf_arr(i,j,k,0) = surf_mf_arr(i,j,0);
279  plot_mf_arr(i,j,k,1) = surf_mf_arr(i,j,1);
280  });
281  }
282 
283  WriteSingleLevelPlotfile(
284  pltname,
285  plot_mf,
286  varnames_plot_mf,
287  geom[0],
288  time,
289  0 // level
290  );*/
291 }
void FillSurfaceStateMultiFabs(const int lev, const std::string &filename, amrex::Vector< amrex::MultiFab > &surface_state)
Definition: ERF_SurfaceDataInterpolation.cpp:26
amrex::Real hindcast_data_interval_in_hrs
Time interval between hindcast data files [hr].
Definition: ERF_DataStruct.H:2313
std::string hindcast_surface_data_dir
Directory containing hindcast surface boundary data.
Definition: ERF_DataStruct.H:2312

◆ 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
20 {
21  //
22  // We need to FillPatch the coarse level before assessing whether to regrid
23  // We have not done the swap yet so we fill the "new" which will become the "old"
24  //
25  MultiFab& S_new = vars_new[lev][Vars::cons];
26  MultiFab& U_new = vars_new[lev][Vars::xvel];
27  MultiFab& V_new = vars_new[lev][Vars::yvel];
28  MultiFab& W_new = vars_new[lev][Vars::zvel];
29 
30 #ifdef ERF_USE_NETCDF
31  //
32  // Since we now only read in a subset of the time slices in wrfbdy and
33  // wrflowinp, we need to check whether it's time to read in more.
34  //
35  bool use_moist = (solverChoice.moisture_type != MoistureType::None);
36  if (solverChoice.use_real_bcs && (lev==0))
37  {
38  MultiFab r_hse(base_state[lev], make_alias, BaseState::r0_comp, 1);
39  Array<MultiFab*, AMREX_SPACEDIM> area_vec = {ax[lev].get(), ay[lev].get(), az[lev].get()};
40 
41  int ntimes = bdy_data_xlo.size();
42  double time_since_start_bdy = time + start_time - start_bdy_time;
43  int n_time_old = std::min(static_cast<int>( (time_since_start_bdy ) / bdy_time_interval), ntimes-1);
44  int n_time_new = std::min(static_cast<int>( (time_since_start_bdy+dt[lev]) / bdy_time_interval), ntimes-1);
45  auto repack_runtime_bdy = [&] (const int itime) {
46  const bool separate_hydrometeors = solverChoice.use_wrf_bdy_qc_qi &&
48  repack_wrfbdy_to_realbdy(bdy_data_xlo[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
49  repack_wrfbdy_to_realbdy(bdy_data_xhi[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
50  repack_wrfbdy_to_realbdy(bdy_data_ylo[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
51  repack_wrfbdy_to_realbdy(bdy_data_yhi[itime], solverChoice.use_wrf_bdy_qc_qi, separate_hydrometeors);
52  };
53 
54  for (int itime = 0; itime < ntimes; itime++)
55  {
56  /*
57  if (bdy_data_xlo[itime].size() > 0) {
58  amrex::Print() << "HAVE BDY DATA AT TIME " << itime << std::endl;
59  } else {
60  amrex::Print() << " NO BDY DATA AT TIME " << itime << std::endl;
61  }
62  */
63 
64  // Note that we never release itime == 0 because it is used for the spatial interpolation at later times
65  bool clear_itime = (itime > 0 && itime < n_time_old);
66 
67  if (clear_itime && bdy_data_xlo[itime].size() > 0) {
68  bdy_data_xlo[itime].clear();
69  bdy_data_xhi[itime].clear();
70  bdy_data_ylo[itime].clear();
71  bdy_data_yhi[itime].clear();
72  //amrex::Print() << "CLEAR BDY DATA AT TIME " << itime << std::endl;
73  }
74 
75  bool need_itime = (itime >= n_time_old && itime <= n_time_new+1);
76  //if (need_itime) { amrex::Print() << "NEED BDY DATA AT TIME " << itime << std::endl; }
77 
78  // Handle erfbdy files (AMReX native format).
79  if (use_erfbdy) {
80  if (bdy_data_xlo[itime].size() == 0 && need_itime) {
82  bdy_data_xlo, bdy_data_xhi,
83  bdy_data_ylo, bdy_data_yhi,
85  repack_runtime_bdy(itime);
86  }
87  // Handle wrfbdy files (NetCDF format).
88  } else {
89  if (bdy_data_xlo[itime].size() == 0 && need_itime) {
90  bool is_anelastic = (solverChoice.anelastic[0] == 1);
91  read_and_convert_from_wrfbdy(itime,nc_bdy_file,bdy_data_xlo,bdy_data_xhi,bdy_data_ylo,bdy_data_yhi,
94  r_hse, area_vec, geom[lev], use_moist,
100  real_width, bdy_time_interval, is_anelastic);
101  if (itime == ntimes-1 && itime > 0) {
102  repack_runtime_bdy(itime-1);
103  }
104  repack_runtime_bdy(itime);
105  }
106  } // use_erfbdy
107  } // itime
108  } // use_real_bcs && lev == 0
109 
110  if (!nc_low_file.empty() && (lev==0))
111  {
112  int ntimes = low_data_zlo.size();
113  double time_since_start_low = time + start_time - start_low_time;
114  int n_time_old = std::min(static_cast<int>( (time_since_start_low ) / low_time_interval), ntimes-1);
115  int n_time_new = std::min(static_cast<int>( (time_since_start_low+dt[lev]) / low_time_interval), ntimes-1);
116 
117  for (int itime = 0; itime < ntimes; itime++)
118  {
119  /*
120  if (low_data_zlo[itime].size() > 0) {
121  amrex::Print() << "HAVE LOW DATA AT TIME " << itime << std::endl;
122  } else {
123  amrex::Print() << " NO LOW DATA AT TIME " << itime << std::endl;
124  }
125  */
126 
127  bool clear_itime = (itime < n_time_old);
128 
129  if (clear_itime && low_data_zlo[itime].size() > 0) {
130  low_data_zlo[itime].clear();
131  //amrex::Print() << "CLEAR LOW DATA AT TIME " << itime << std::endl;
132  }
133 
134  bool need_itime = (itime >= n_time_old && itime <= n_time_new+1);
135  //if (need_itime) { amrex::Print() << "NEED LOW DATA AT TIME " << itime << std::endl; }
136 
137  if (low_data_zlo[itime].size() == 0 && need_itime) {
138  read_from_wrflow(itime, nc_low_file, geom[lev].Domain(), low_data_zlo);
139 
140  update_sst_tsk(itime, geom[lev], ba2d[lev],
141  sst_lev[lev], tsk_lev[lev],
142  m_SurfaceLayer[Orientation(Direction::z, Orientation::low)], low_data_zlo,
143  S_new, *mf_PSFC[lev],
144  solverChoice.rdOcp, lmask_lev[lev][0], use_moist);
145  }
146  } // itime
147  } // have nc_low_file && lev == 0
148 #endif
149 
150  //
151  // NOTE: the momenta here are not fillpatched (they are only used as scratch space)
152  //
153  if (lev == 0) {
154  FillPatchCrseLevel(lev, time, {&S_new, &U_new, &V_new, &W_new});
155  } else if (lev < finest_level) {
156  FillPatchFineLevel(lev, time, {&S_new, &U_new, &V_new, &W_new},
157  {&S_new, &rU_new[lev], &rV_new[lev], &rW_new[lev]},
158  base_state[lev], base_state[lev]);
159  }
160 
161  if (regrid_int > 0) // We may need to regrid
162  {
163  // help keep track of whether a level was already regridded
164  // from a coarser level call to regrid
165  static Vector<int> last_regrid_step(max_level+1, 0);
166 
167  // regrid changes level "lev+1" so we don't regrid on max_level
168  // also make sure we don't regrid fine levels again if
169  // it was taken care of during a coarser regrid
170  if (lev < max_level)
171  {
172  if ( (istep[lev] % regrid_int == 0) && (istep[lev] > last_regrid_step[lev]) )
173  {
174  // regrid could add newly refine levels (if finest_level < max_level)
175  // so we save the previous finest level index
176  int old_finest = finest_level;
177 
178  if (solverChoice.coupling_type == CouplingType::TwoWay &&
179  solverChoice.moisture_type != MoistureType::None &&
180  Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian &&
181  finest_level >= 1) {
182  micro->AverageDownMicroVars(finest_level);
183  for (int flev = finest_level-1; flev >= lev; --flev) {
185  }
186  }
187 
188 #ifdef ERF_USE_PARTICLES
189  // Snapshot the per-level particle BoxArrays before regrid so
190  // we can identify cells that lost fine coverage and merge
191  // them down to the coarse-level SD density.
192  Vector<BoxArray> old_pc_ba;
193  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
194  auto* pc = dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer();
195  AMREX_ALWAYS_ASSERT(pc != nullptr);
196  old_pc_ba.resize(old_finest + 1);
197  for (int k = 0; k <= old_finest; k++) {
198  old_pc_ba[k] = pc->ParticleBoxArray(k);
199  }
200  }
201 #endif
202 
203  regrid(lev, static_cast<Real>(time));
204 
205 #ifdef ERF_USE_PARTICLES
206  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
207  auto* pc = dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer();
208  AMREX_ALWAYS_ASSERT(pc != nullptr);
209  // Sync the particle container's per-level storage with
210  // the post-regrid BoxArrays/DistributionMaps before any
211  // iMultiFab-based work touches it. Note: this is the
212  // bare ParticleContainer::Redistribute (no SplitMerge);
213  // tag-based splitting and merging still run below.
214  pc->Redistribute();
215  // Split super-droplets that ended up on a level deeper
216  // than their tag indicates (cumulative cascading split
217  // for L0-natives that landed directly on L1 or L2 after
218  // the regrid created multiple new levels in one shot).
219  pc->SplitParticlesForRefinement(finest_level);
220  }
221  // Redistribute moves split daughters to their destination
222  // sub-cells and runs each species' SplitMergeAtLevelBoundary,
223  // whose per-level tag-normalizing merge sweep cleans up any
224  // leftover super-droplets from levels that have just vanished.
225  particleData.Redistribute(z_phys_nd);
226 
227  if (Microphysics::modelType(solverChoice.moisture_type) == MoistureModelType::Lagrangian) {
228  auto* pc = dynamic_cast<LagrangianMicrophysics&>(*micro).getParticleContainer();
229  AMREX_ALWAYS_ASSERT(pc != nullptr);
230  // Reduce SD count in cells that lost fine-level coverage.
231  // Walks old_finest..1 so each level's masked merge runs
232  // while clev = k-1 still holds the just-moved fines.
233  for (int k = old_finest; k >= 1; k--) {
234  pc->MergeParticlesAtDerefinement(k, old_pc_ba[k], refRatio(k-1));
235  }
236  }
237 #endif
238 
239  // mark that we have regridded this level already
240  for (int k = lev; k <= finest_level; ++k) {
241  last_regrid_step[k] = istep[k];
242  }
243 
244  // if there are newly created levels, set the time step
245  for (int k = old_finest+1; k <= finest_level; ++k) {
246  dt[k] = dt[k-1] / static_cast<double>(nsubsteps[k]);
247  }
248  } // if
249  } // lev
250  }
251 
252  // Update what we call "old" and "new" time
253  t_old[lev] = t_new[lev];
254  t_new[lev] += dt[lev];
255 
256  if (Verbose()) {
257  amrex::Print() << "[Level " << lev << " step " << istep[lev]+1 << "] ";
258  amrex::Print() << std::setprecision(timeprecision)
259  << "ADVANCE from elapsed time = " << t_old[lev] << " to " << t_new[lev]
260  << " with dt = " << dt[lev] << std::endl;
261  }
262 
263 #ifdef ERF_USE_WW3_COUPLING
264  amrex::Print() << " About to call send_to_ww3 from ERF_Timestep" << std::endl;
265  send_to_ww3(lev);
266  amrex::Print() << " About to call read_waves from ERF_Timestep" << std::endl;
267  read_waves(lev);
268  //send_to_ww3(lev);
269  //read_waves(lev);
270  //send_to_ww3(lev);
271 #endif
272 
273  // Advance a single level for a single time step
274  Advance(lev, time, dt[lev], istep[lev], nsubsteps[lev]);
275 
276  ++istep[lev];
277 
278  if (Verbose()) {
279  amrex::Print() << "[Level " << lev << " step " << istep[lev] << "] ";
280  amrex::Print() << "Advanced " << CountCells(lev) << " cells" << std::endl;
281  }
282 
283  if (lev < finest_level)
284  {
285  // recursive call for next-finer level
286  for (int i = 1; i <= nsubsteps[lev+1]; ++i)
287  {
288  double strt_time_for_fine = time + (i-1)*dt[lev+1];
289  timeStep(lev+1, strt_time_for_fine, i);
290  }
291  }
292 
293  if ( verbose && lev == 0 && solverChoice.moisture_type != MoistureType::None) {
294  amrex::Print() << "Cloud fraction " << time << " " << cloud_fraction(time) << std::endl;
295  }
296 }
void Advance(int lev, double time, double dt_lev, int iteration, int ncycle)
Definition: ERF_Advance.cpp:21

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

Calculate the cell-centered turbulent perturbation amplitude.

Parameters
levInteger specifying the current level
43 {
44  // Accessing data
45  auto& lev_new = vars_new[lev];
46 
47  // Creating local data
48  int ncons = lev_new[Vars::cons].nComp();
49  MultiFab cons_data(lev_new[Vars::cons], make_alias, 0, ncons);
50 
51  // Defining BoxArray type
52  auto m_ixtype = cons_data.boxArray().ixType();
53 
54 #ifdef _OPENMP
55 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
56 #endif
57  for (MFIter mfi(lev_new[Vars::cons], TileNoZ()); mfi.isValid(); ++mfi) {
58  const Box &bx = mfi.validbox();
59  const auto &cons_pert_arr = cons_data.array(mfi); // Address of perturbation array
60  const amrex::Array4<const amrex::Real> &pert_cell = turbPert.pb_cell[lev].array(mfi); // per-cell perturbation stored in structure
61 
62  turbPert.apply_tpi(lev, bx, RhoTheta_comp, m_ixtype, cons_pert_arr, pert_cell);
63  } // mfi
64 }
Here is the call graph for this function:

◆ turbPert_amplitude_w()

void ERF::turbPert_amplitude_w ( const int  lev)
private

Calculate the turbulent perturbation amplitude for vertical velocity.

Parameters
levInteger specifying the current level
73 {
74  // Accessing data
75  auto& lev_new = vars_new[lev];
76 
77  // Creating local data
78  int ncomps = lev_new[Vars::zvel].nComp();
79  MultiFab wvel_data(lev_new[Vars::zvel], make_alias, 0, ncomps);
80 
81  // Defining BoxArray type
82  auto m_ixtype = wvel_data.boxArray().ixType();
83 
84 #ifdef _OPENMP
85 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
86 #endif
87  for (MFIter mfi(lev_new[Vars::zvel], TileNoZ()); mfi.isValid(); ++mfi) {
88  const Box &bx = mfi.validbox();
89  const auto &wvel_pert_arr = wvel_data.array(mfi); // Address of perturbation array
90  const amrex::Array4<const amrex::Real> &pert_cell = turbPert.pb_cell[lev].array(mfi); // per-cell perturbation stored in structure
91 
92  turbPert.apply_tpi(lev, bx, -1, m_ixtype, wvel_pert_arr, pert_cell);
93  } // mfi
94 }
Here is the call graph for this function:

◆ turbPert_update()

void ERF::turbPert_update ( const int  lev,
const double  local_dt 
)
private

Update turbulent perturbation timing and amplitudes for one AMR level.

Parameters
levInteger specifying the current level
local_dtLocal timestep used to update the perturbation state
19 {
20  // Accessing data
21  auto& lev_new = vars_new[lev];
22 
23  // Create aliases to state data to pass to calc_tpi_update
24  int ncons = lev_new[Vars::cons].nComp();
25  MultiFab cons_data(lev_new[Vars::cons], make_alias, 0, ncons);
26  MultiFab xvel_data(lev_new[Vars::xvel], make_alias, 0, 1);
27  MultiFab yvel_data(lev_new[Vars::yvel], make_alias, 0, 1);
28 
29  // Computing perturbation update time
30  turbPert.calc_tpi_update(lev, local_dt, xvel_data, yvel_data, cons_data);
31 
32  Print() << "Successfully initialized turbulent perturbation update time and amplitude at level "
33  << lev << " with type: " << turbPert.pt_type[lev] << "\n";
34 }
amrex::Vector< int > pt_type
Integer perturbation type for each AMR level.
Definition: ERF_TurbPertStruct.H:754

◆ update_box_for_refinement()

void ERF::update_box_for_refinement ( std::string &  ref_prefix,
int &  lev_for_box,
amrex::RealBox &  real_box,
const double  time 
)

Update the refinement box position for moving grids.

Parameters
[in]ref_prefixPrefix for parsing input parameters.
[out]lev_for_boxLevel associated with the refinement box.
[out]real_boxUpdated real-space coordinates of the refinement box.
[in]timeCurrent simulation time.
373 {
374  ParmParse ppr(ref_prefix);
375 
376  Vector<double> move_start_time, move_stop_time;
377  int ni = ppr.queryarr("move_start_time", move_start_time);
378  int nj = ppr.queryarr("move_stop_time" , move_stop_time);
379  if (ni != nj) {
380  amrex::Print() << "Must be same number of start times as stop times for moving grids" << std::endl;
381  amrex::Abort();
382  }
383  for (int i = 0; i < ni; i++) {
384  if (move_stop_time[i] <= move_start_time[i]) {
385  amrex::Print() << "start time for interval " << i << " is " << move_start_time[i] << std::endl;
386  amrex::Print() << "stop time for interval " << i << " is " << move_stop_time[i] << std::endl;
387  amrex::Abort("moving grid: stop time must be greater than start time");
388  }
389  }
390  for (int i = 1; i < ni; i++) {
391  if (move_start_time[i] < move_stop_time[i-1]) {
392  amrex::Print() << "start time for interval " << i << " is " << move_start_time[i] << std::endl;
393  amrex::Print() << "stop time for interval " << i-1 << " is " << move_stop_time[i-1] << std::endl;
394  amrex::Abort("moving grid: stop time must be less than start time of the next interval");
395  }
396  }
397 
398  Vector<Real> move_speed_x, move_speed_y;
399  int ni2 = ppr.queryarr("move_speed_x", move_speed_x);
400  int nj2 = ppr.queryarr("move_speed_y", move_speed_y);
401  if (ni2 != nj2 ) {
402  amrex::Print() << "Must be same number of speeds in x- and y-directions" << std::endl;
403  amrex::Abort();
404  }
405  if (ni != ni2 ) {
406  amrex::Print() << "Must be same number of speeds as time intervals" << std::endl;
407  amrex::Abort();
408  }
409 
410  Real offset_x = zero;
411  Real offset_y = zero;
412 
413  for (int i = 0; i < ni; i++) {
414  if (time > move_start_time[i]) {
415  offset_x += move_speed_x[i] * static_cast<Real>(std::min(time,move_stop_time[i]) - move_start_time[i]);
416  offset_y += move_speed_y[i] * static_cast<Real>(std::min(time,move_stop_time[i]) - move_start_time[i]);
417  }
418  }
419 
420  RealBox orig_real_box;
421  read_box_for_refinement (ref_prefix, lev_for_box, orig_real_box);
422 
423  Real xlo = orig_real_box.lo(0) + offset_x; Real ylo = orig_real_box.lo(1) + offset_y;
424  Real xhi = orig_real_box.hi(0) + offset_x; Real yhi = orig_real_box.hi(1) + offset_y;
425  Real zlo = orig_real_box.lo(2); Real zhi = orig_real_box.hi(2);
426 
427  real_box.setLo(RealVect(xlo,ylo,zlo));
428  real_box.setHi(RealVect(xhi,yhi,zhi));
429 }
@ ni
Definition: ERF_Morrison.H:48

◆ update_diffusive_arrays()

void ERF::update_diffusive_arrays ( int  lev,
const amrex::BoxArray &  ba,
const amrex::DistributionMapping &  dm 
)
private
685 {
686  // ********************************************************************************************
687  // Diffusive terms
688  // ********************************************************************************************
689  bool l_use_eb = (SolverChoice::terrain_type == TerrainType::EB);
690  bool l_use_terrain = (SolverChoice::terrain_type != TerrainType::None && !l_use_eb);
691  bool l_use_kturb = solverChoice.turbChoice[lev].use_kturb;
692  bool l_use_diff = ( (solverChoice.diffChoice.molec_diff_type != MolecDiffType::None) ||
693  l_use_kturb );
694  bool l_need_SmnSmn = solverChoice.turbChoice[lev].use_keqn;
695  bool l_use_moist = ( solverChoice.moisture_type != MoistureType::None );
696  bool l_rotate = ( solverChoice.use_rotate_surface_flux );
697  bool l_Surf_X = phys_bc_type[Orientation::xlo()] == ERF_BC::surface_layer || phys_bc_type[Orientation::xhi()] == ERF_BC::surface_layer;
698  bool l_Surf_Y = phys_bc_type[Orientation::ylo()] == ERF_BC::surface_layer || phys_bc_type[Orientation::yhi()] == ERF_BC::surface_layer;
699 
700 
701  bool l_implicit_diff = (solverChoice.vert_implicit_fac[lev][0] > 0 ||
702  solverChoice.vert_implicit_fac[lev][1] > 0 ||
703  solverChoice.vert_implicit_fac[lev][2] > 0);
704 
705  bool l_eb_surface_layer = (l_use_eb && solverChoice.ebChoice.eb_boundary_type == EBBoundaryType::SurfaceLayer);
706 
707  BoxArray ba12 = convert(ba, IntVect(1,1,0));
708  BoxArray ba13 = convert(ba, IntVect(1,0,1));
709  BoxArray ba23 = convert(ba, IntVect(0,1,1));
710 
711  Tau[lev].resize(9);
712  Tau_corr[lev].resize(3);
713 
714  // Always resize Tau_EB structure, even if not used, because other code checks nullptr
715  Tau_EB[lev].resize(2); // tau_eb13 and tau_eb23
716  for (int comp = 0; comp < 2; ++comp) {
717  Tau_EB[lev][comp].resize(3); // xface, yface, zface
718  }
719 
720  if (l_use_diff) {
721  //
722  // NOTE: We require ghost cells in the vertical when allowing grids that don't
723  // cover the entire vertical extent of the domain at this level
724  //
725  Tau[lev][TauType::tau11] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau11]->setVal(zero);
726  Tau[lev][TauType::tau22] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau22]->setVal(zero);
727  Tau[lev][TauType::tau33] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau33]->setVal(zero);
728 
729  Tau[lev][TauType::tau12] = std::make_unique<MultiFab>( ba12, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau12]->setVal(zero);
730  Tau[lev][TauType::tau13] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau13]->setVal(zero);
731  Tau[lev][TauType::tau23] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau23]->setVal(zero);
732  if (l_use_terrain || (l_Surf_X || l_Surf_Y)) {
733  Tau[lev][TauType::tau21] = std::make_unique<MultiFab>( ba12, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau21]->setVal(zero);
734  Tau[lev][TauType::tau31] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau31]->setVal(zero);
735  Tau[lev][TauType::tau32] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau32]->setVal(zero);
736  } else if (l_implicit_diff) {
737  Tau[lev][TauType::tau31] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau31]->setVal(zero);
738  Tau[lev][TauType::tau32] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); Tau[lev][TauType::tau32]->setVal(zero);
739  } else {
740  Tau[lev][TauType::tau21] = nullptr;
741  Tau[lev][TauType::tau31] = nullptr;
742  Tau[lev][TauType::tau32] = nullptr;
743  }
744 
745  // EB diffusive stresses - allocate for all three staggered grids
746  if (l_eb_surface_layer) {
747  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::xface] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
748  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::yface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
749  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::zface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
750  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::xface]->setVal(0.);
751  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::yface]->setVal(0.);
752  Tau_EB[lev][EBTauType::tau_eb13][EBGridType::zface]->setVal(0.);
753 
754  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::xface] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
755  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::yface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
756  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::zface] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
757  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::xface]->setVal(0.);
758  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::yface]->setVal(0.);
759  Tau_EB[lev][EBTauType::tau_eb23][EBGridType::zface]->setVal(0.);
760  } else {
761  for (int comp = 0; comp < 2; ++comp) {
762  for (int grid = 0; grid < 3; ++grid) {
763  Tau_EB[lev][comp][grid] = nullptr;
764  }
765  }
766  }
767 
768  if (l_implicit_diff && solverChoice.implicit_momentum_diffusion)
769  {
770  Tau_corr[lev][0] = std::make_unique<MultiFab>( ba13, dm, 1, IntVect(1,1,1) ); // Tau31
771  Tau_corr[lev][1] = std::make_unique<MultiFab>( ba23, dm, 1, IntVect(1,1,1) ); // Tau32
772  Tau_corr[lev][0]->setVal(zero);
773  Tau_corr[lev][1]->setVal(zero);
774 #ifdef ERF_IMPLICIT_W
775  Tau_corr[lev][2] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) ); // Tau33
776  Tau_corr[lev][2]->setVal(zero);
777 #else
778  Tau_corr[lev][2] = nullptr;
779 #endif
780  } else {
781  Tau_corr[lev][0] = nullptr;
782  Tau_corr[lev][1] = nullptr;
783  Tau_corr[lev][2] = nullptr;
784  }
785 
786  SFS_hfx1_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
787  SFS_hfx2_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
788  SFS_hfx3_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
789  SFS_diss_lev[lev] = std::make_unique<MultiFab>( ba , dm, 1, IntVect(1,1,1) );
790  SFS_hfx1_lev[lev]->setVal(zero);
791  SFS_hfx2_lev[lev]->setVal(zero);
792  SFS_hfx3_lev[lev]->setVal(zero);
793  SFS_diss_lev[lev]->setVal(zero);
794 
795  // EB heat fluxes
796  if (l_use_eb) {
797  hfx3_EB[lev] = std::make_unique<MultiFab>( ba, dm, 1, IntVect(1,1,1) );
798  hfx3_EB[lev]->setVal(zero);
799  } else {
800  hfx3_EB[lev] = nullptr;
801  }
802 
803  if (l_use_moist) {
804  SFS_q1fx3_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
805  SFS_q2fx3_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,0,1)), dm, 1, IntVect(1,1,1) );
806  SFS_q1fx3_lev[lev]->setVal(zero);
807  SFS_q2fx3_lev[lev]->setVal(zero);
808  if (l_rotate || (l_Surf_X || l_Surf_Y)) {
809  SFS_q1fx1_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(1,0,0)), dm, 1, IntVect(1,1,1) );
810  SFS_q1fx2_lev[lev] = std::make_unique<MultiFab>( convert(ba,IntVect(0,1,0)), dm, 1, IntVect(1,1,1) );
811  SFS_q1fx1_lev[lev]->setVal(zero);
812  SFS_q1fx2_lev[lev]->setVal(zero);
813  } else {
814  SFS_q1fx1_lev[lev] = nullptr;
815  SFS_q1fx2_lev[lev] = nullptr;
816  }
817  } else {
818  SFS_q1fx1_lev[lev] = nullptr;
819  SFS_q1fx2_lev[lev] = nullptr;
820  SFS_q1fx3_lev[lev] = nullptr;
821  SFS_q2fx3_lev[lev] = nullptr;
822  }
823  } else {
824  for (int i = 0; i < 9; i++) {
825  Tau[lev][i] = nullptr;
826  }
827  SFS_hfx1_lev[lev] = nullptr; SFS_hfx2_lev[lev] = nullptr; SFS_hfx3_lev[lev] = nullptr;
828  SFS_q1fx1_lev[lev] = nullptr; SFS_q1fx2_lev[lev] = nullptr; SFS_q1fx3_lev[lev] = nullptr;
829  SFS_q2fx3_lev[lev] = nullptr;
830  SFS_diss_lev[lev] = nullptr;
831  }
832 
833  if (l_use_kturb) {
834  eddyDiffs_lev[lev] = std::make_unique<MultiFab>(ba, dm, EddyDiff::NumDiffs, 2);
835  eddyDiffs_lev[lev]->setVal(zero);
836  if(l_need_SmnSmn) {
837  SmnSmn_lev[lev] = std::make_unique<MultiFab>( ba, dm, 1, 0 );
838  SmnSmn_lev[lev]->setVal(zero);
839  } else {
840  SmnSmn_lev[lev] = nullptr;
841  }
842  } else {
843  eddyDiffs_lev[lev] = nullptr;
844  SmnSmn_lev[lev] = nullptr;
845  }
846 }
@ tau_eb23
Definition: ERF_EBStruct.H:22
@ tau_eb13
Definition: ERF_EBStruct.H:22
@ yface
Definition: ERF_EBStruct.H:29
@ zface
Definition: ERF_EBStruct.H:29
@ xface
Definition: ERF_EBStruct.H:29
EBBoundaryType eb_boundary_type
Boundary condition model applied on embedded-boundary surfaces.
Definition: ERF_EBStruct.H:75
EBChoice ebChoice
Embedded-boundary options.
Definition: ERF_DataStruct.H:1975

◆ update_terrain_arrays()

void ERF::update_terrain_arrays ( int  lev)
1266 {
1267  if (SolverChoice::mesh_type == MeshType::StretchedDz ||
1268  SolverChoice::mesh_type == MeshType::VariableDz) {
1269  make_J(geom[lev],*z_phys_nd[lev],*detJ_cc[lev]);
1270  make_areas(geom[lev],*z_phys_nd[lev],*ax[lev],*ay[lev],*az[lev]);
1271  make_zcc(geom[lev],*z_phys_nd[lev],*z_phys_cc[lev]);
1272  } else { // MeshType::ConstantDz
1273  if (SolverChoice::terrain_type == TerrainType::EB) {
1274  const auto& ebfact = *eb[lev]->get_const_factory();
1275  const MultiFab& volfrac = ebfact.getVolFrac();
1276  detJ_cc[lev] = std::make_unique<MultiFab>(volfrac, amrex::make_alias, 0, volfrac.nComp());
1277  }
1278  }
1279 }
void make_areas(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &ax, MultiFab &ay, MultiFab &az)
Definition: ERF_TerrainMetrics.cpp:981
void make_J(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &detJ_cc)
Definition: ERF_TerrainMetrics.cpp:943
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 
)

Compute a volume-weighted column sum of MultiFab data.

Parameters
levCurrent level.
[in]mf_to_be_summedMultiFab to be summed.
[in]compComponent index to be summed.
[out]mf_2dMultiFab to store the resulting column sums.
[in]dJVolume weighting due to metric terms.
94 {
95  BL_PROFILE("ERF::volWgtSumColumnMF()");
96 
97  mf_2d.setVal(0.);
98 
99  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
100  // m is the map scale factor at cell centers
101  //
102  // NOTE: TileNoZ, not TilingIfNotGPU: the atomic adds accumulate a whole column into
103  // dst_arr(i,j,0), so if the grid were tiled in z the tiles covering one column
104  // would add into the same entry concurrently. On the host HostDevice::Atomic::Add
105  // is "#pragma omp atomic" -- atomic but unordered -- so the column sum would then
106  // depend on thread scheduling in its last bits. Keeping each column inside a
107  // single tile makes the k accumulation serial and the result independent of both
108  // the tile size and the thread count.
109  //
110 #ifdef _OPENMP
111 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
112 #endif
113  for (MFIter mfi(mf_to_be_summed, TileNoZ()); mfi.isValid(); ++mfi) {
114  const Box& bx = mfi.tilebox();
115  const auto dst_arr = mf_2d.array(mfi);
116  const auto src_arr = mf_to_be_summed.array(mfi);
117  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
118  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
119  {
120  amrex::HostDevice::Atomic::Add(&dst_arr(i,j,0),src_arr(i,j,k,comp));
121  });
122  } else {
123  const auto& dJ_arr = dJ.const_array(mfi);
124  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
125  {
126  amrex::HostDevice::Atomic::Add(&dst_arr(i,j,0),src_arr(i,j,k,comp)*dJ_arr(i,j,k));
127  });
128  }
129  } // mfi
130 
131  auto const& dx = geom[lev].CellSizeArray();
132 
133  mf_2d.mult(dx[2]);
134 }
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)
26 {
27  BL_PROFILE("ERF::volWgtSumMF()");
28 
29  Real sum = zero;
30  MultiFab tmp(mf_to_be_summed.boxArray(), mf_to_be_summed.DistributionMap(), 1, 0);
31 
32  // The quantity that is conserved is not (rho S), but rather (rho S / m^2) where
33  // m is the map scale factor at cell centers
34 #ifdef _OPENMP
35 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
36 #endif
37  for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
38  const Box& bx = mfi.tilebox();
39  const auto dst_arr = tmp.array(mfi);
40  const auto src_arr = mf_to_be_summed.array(mfi);
41  const auto& mfx_arr = mfmx.const_array(mfi);
42  const auto& mfy_arr = mfmy.const_array(mfi);
43 
44  if (SolverChoice::terrain_type != TerrainType::EB) {
45  if (SolverChoice::mesh_type == MeshType::ConstantDz) {
46  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
47  {
48  dst_arr(i,j,k,0) = src_arr(i,j,k,comp) / (mfx_arr(i,j,0)*mfy_arr(i,j,0));
49  });
50  } else {
51  const auto& dJ_arr = dJ.const_array(mfi);
52  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
53  {
54  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));
55  });
56  }
57  } else {
58  const auto& dJ_arr = dJ.const_array(mfi);
59  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
60  {
61  dst_arr(i,j,k,0) = src_arr(i,j,k,comp) * dJ_arr(i,j,k);
62  });
63  }
64 
65  } // mfi
66 
67  if (lev < finest_level && finemask) {
68  MultiFab::Multiply(tmp, *fine_mask[lev+1].get(), 0, 0, 1, 0);
69  }
70 
71  // If local = true then "sum" will be the sum only over the FABs on each rank
72  // If local = false then "sum" will be the sum over the whole MultiFab, and will be broadcast to all ranks
73  sum = tmp.sum(0,local);
74 
75  auto const& dx = geom[lev].CellSizeArray();
76 
77  sum *= dx[0]*dx[1]*dx[2];
78 
79  return sum;
80 }
Here is the call graph for this function:

◆ WeatherDataInterpolation()

void ERF::WeatherDataInterpolation ( const int  lev,
const double  time,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  a_z_phys_nd,
bool  regrid_forces_file_read 
)

Interpolate weather forecast data onto the simulation mesh.

Parameters
[in]levLevel index.
[in]timeCurrent simulation time.
[in]a_z_phys_ndNodal physical height fields across all levels.
[in]regrid_forces_file_readFlag to force reading of forecast files during regridding.
378 {
379 
380  static amrex::Vector<double> next_read_forecast_time;
381  static amrex::Vector<double> last_read_forecast_time;
382 
383  const int nlevs = static_cast<int>(a_z_phys_nd.size());
384 
385  Real hindcast_data_interval = solverChoice.hindcast_data_interval_in_hrs*Real(3600.0);
386 
387  // Initialize static vectors once
388  if (next_read_forecast_time.empty()) {
389  next_read_forecast_time.resize(nlevs, -one);
390  last_read_forecast_time.resize(nlevs, -one);
391  Print() << "Initializing the time vector values here by " << lev << std::endl;
392  }
393 
394  if (next_read_forecast_time[lev] < zero) {
395  int next_multiple = static_cast<int>(time / hindcast_data_interval);
396  next_read_forecast_time[lev] = next_multiple * hindcast_data_interval;
397  last_read_forecast_time[lev] = next_read_forecast_time[lev];
398  }
399 
400  if (time >= next_read_forecast_time[lev] or regrid_forces_file_read) {
401 
402  Print() << "Data reading happening at level " << lev << std::endl;
403 
404  std::string folder = solverChoice.hindcast_boundary_data_dir;
405 
406  // Check if folder exists and is a directory
407  if (!fs::exists(folder) || !fs::is_directory(folder)) {
408  throw std::runtime_error("Error: Folder '" + folder + "' does not exist or is not a directory.");
409  }
410 
411  std::vector<std::string> bin_files;
412 
413  for (const auto& entry : fs::directory_iterator(folder)) {
414  if (!entry.is_regular_file()) continue;
415 
416  std::string fname = entry.path().filename().string();
417  if (fname.size() >= 4 && fname.substr(fname.size() - 4) == ".bin") {
418  bin_files.push_back(entry.path().string());
419  }
420  }
421  std::sort(bin_files.begin(), bin_files.end());
422 
423  // Check if no .bin files were found
424  if (bin_files.empty()) {
425  throw std::runtime_error("Error: No .bin files found in folder '" + folder + "'.");
426  }
427 
428  std::string filename1, filename2;
429 
430  int idx1 = static_cast<int>(time / hindcast_data_interval);
431  int idx2 = static_cast<int>(time / hindcast_data_interval)+1;
432  Print() << "Reading weather data " << time << " " << idx1 << " " << idx2 <<" " << bin_files.size() << std::endl;
433 
434  if (idx2 >= static_cast<int>(bin_files.size())) {
435  throw std::runtime_error("Error: Not enough .bin files to cover time " + std::to_string(time));
436  }
437 
438  filename1 = bin_files[idx1];
439  filename2 = bin_files[idx2];
440 
441  FillForecastStateMultiFabs(lev, filename1, a_z_phys_nd[lev], forecast_state_1);
442  FillForecastStateMultiFabs(lev, filename2, a_z_phys_nd[lev], forecast_state_2);
443 
444  // Create the time-interpolated forecast state
445  //CreateForecastStateMultiFabs(forecast_state_interp);
446  if(!regrid_forces_file_read){
447  last_read_forecast_time[lev] = next_read_forecast_time[lev];
448  next_read_forecast_time[lev] += hindcast_data_interval;
449  Print() << "Next forecast time getting updated here " << std::endl;
450  }
451  }
452 
453  double prev_read_time = last_read_forecast_time[lev];
454  Real alpha1 = static_cast<Real>(1.0 - (time - prev_read_time)/hindcast_data_interval);
455  Real alpha2 = one - alpha1;
456 
457  amrex::Print()<< "The values of alpha1 and alpha2 are " << alpha1 << " "<< alpha2 <<std::endl;
458 
459  if (alpha1 < zero || alpha1 > one ||
460  alpha2 < zero || alpha2 > one)
461  {
462  std::stringstream ss;
463  ss << "Interpolation weights for hindcast files are incorrect: "
464  << "alpha1 = " << alpha1 << ", alpha2 = " << alpha2;
465  Abort(ss.str());
466  }
467 
468  MultiFab& erf_mf_cons = forecast_state_interp[lev][Vars::cons];
469  MultiFab& erf_mf_xvel = forecast_state_interp[lev][Vars::xvel];
470  MultiFab& erf_mf_yvel = forecast_state_interp[lev][Vars::yvel];
471  //MultiFab& erf_mf_zvel = forecast_state_interp[0][Vars::zvel];
472  MultiFab& erf_mf_latlon = forecast_state_interp[lev][4];
473 
474  // Fill the time-interpolated forecast states
475  MultiFab::LinComb(forecast_state_interp[lev][Vars::cons],
476  alpha1, forecast_state_1[lev][Vars::cons], 0,
477  alpha2, forecast_state_2[lev][Vars::cons], 0,
478  0, erf_mf_cons.nComp(), forecast_state_interp[lev][Vars::cons].nGrow());
479  MultiFab::LinComb(forecast_state_interp[lev][Vars::xvel],
480  alpha1, forecast_state_1[lev][Vars::xvel], 0,
481  alpha2, forecast_state_2[lev][Vars::xvel], 0,
482  0, erf_mf_xvel.nComp(), forecast_state_interp[lev][Vars::xvel].nGrow());
483  MultiFab::LinComb(forecast_state_interp[lev][Vars::yvel],
484  alpha1, forecast_state_1[lev][Vars::yvel], 0,
485  alpha2, forecast_state_2[lev][Vars::yvel], 0,
486  0, erf_mf_yvel.nComp(), forecast_state_interp[lev][Vars::yvel].nGrow());
487  MultiFab::LinComb(forecast_state_interp[lev][4],
488  alpha1, forecast_state_1[lev][4], 0,
489  alpha2, forecast_state_2[lev][4], 0,
490  0, erf_mf_latlon.nComp(), forecast_state_interp[lev][4].nGrow());
491 
492  /*Vector<std::string> varnames_plot_mf = {
493  "rho", "rhotheta", "rhoqv", "rhoqc", "rhoqr", "xvel", "yvel", "zvel", "latitude", "longitude"
494  }; // Customize variable names
495 
496  std::string pltname = "plt_interp";
497 
498  MultiFab plot_mf(erf_mf_cons.boxArray(), erf_mf_cons.DistributionMap(),
499  10, 0);
500 
501  plot_mf.setVal(0.0);
502 
503  for (MFIter mfi(plot_mf); mfi.isValid(); ++mfi) {
504  const Array4<Real> &plot_mf_arr = plot_mf.array(mfi);
505  const Array4<Real> &erf_mf_cons_arr = erf_mf_cons.array(mfi);
506  const Array4<Real> &erf_mf_xvel_arr = erf_mf_xvel.array(mfi);
507  const Array4<Real> &erf_mf_yvel_arr = erf_mf_yvel.array(mfi);
508  const Array4<Real> &erf_mf_zvel_arr = erf_mf_zvel.array(mfi);
509  const Array4<Real> &erf_mf_latlon_arr = erf_mf_latlon.array(mfi);
510 
511  const Box& bx = mfi.validbox();
512 
513  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
514  plot_mf_arr(i,j,k,0) = erf_mf_cons_arr(i,j,k,Rho_comp);
515  plot_mf_arr(i,j,k,1) = erf_mf_cons_arr(i,j,k,RhoTheta_comp);
516  plot_mf_arr(i,j,k,2) = erf_mf_cons_arr(i,j,k,RhoQ1_comp);
517  plot_mf_arr(i,j,k,3) = erf_mf_cons_arr(i,j,k,RhoQ2_comp);
518  plot_mf_arr(i,j,k,4) = erf_mf_cons_arr(i,j,k,RhoQ3_comp);
519 
520  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;
521  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;
522  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;
523 
524  plot_mf_arr(i,j,k,8) = erf_mf_latlon_arr(i,j,k,0);
525  plot_mf_arr(i,j,k,9) = erf_mf_latlon_arr(i,j,k,1);
526  });
527  }
528 
529 
530  WriteSingleLevelPlotfile(
531  pltname,
532  plot_mf,
533  varnames_plot_mf,
534  geom[0],
535  time,
536  0 // level
537  );*/
538 }
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:72
std::string hindcast_boundary_data_dir
Directory containing hindcast lateral boundary data.
Definition: ERF_DataStruct.H:2312

◆ Write2DPlotFile()

void ERF::Write2DPlotFile ( int  which,
PlotFileType  plotfile_type,
amrex::Vector< std::string >  plot_var_names 
)
646 {
647  const int plot_step = istep[0];
648  const std::string plotfilename = make_2d_plotfile_name(which, plot_step,
651 
652  const auto output_descriptors =
654  plot_var_names, solverChoice);
655 
656  Vector<std::string> varnames;
657  varnames.reserve(output_descriptors.size());
658  for (const auto& descriptor : output_descriptors) {
659  varnames.push_back(descriptor.name);
660  }
661  const int ncomp_mf = static_cast<int>(varnames.size());
662 
663  if (ncomp_mf == 0) return;
664 
665  // Vector of MultiFabs for cell-centered data
666  Vector<MultiFab> mf(finest_level+1);
667  for (int lev = 0; lev <= finest_level; ++lev) {
668  mf[lev].define(ba2d[lev], dmap[lev], ncomp_mf, 0);
669  }
670 
671 
672  // **********************************************************************************************
673  // (Effectively) 2D arrays
674  // **********************************************************************************************
675  for (int lev = 0; lev <= finest_level; ++lev) {
676  FillPlot2DVars(lev, plot_var_names, varnames, output_descriptors, mf[lev], ncomp_mf);
677  }
678 
679  Vector<Geometry> my_geom = make_2d_plot_geometries(geom, finest_level);
680 
681  if (plotfile_type == PlotFileType::Amrex)
682  {
683  Print() << "Writing 2D native plotfile " << plotfilename << "\n";
684  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
685  GetVecOfConstPtrs(mf),
686  varnames, my_geom, static_cast<Real>(t_new[0]), istep, refRatio());
687  // Native AMReX 2D plotfiles write a JSON sidecar with catalog
688  // metadata for the selected output variables only.
689  plotfile2d::write_2d_metadata_json(plotfilename, output_descriptors);
691  istep[0], t_new[0]);
692 
693 #ifdef ERF_USE_NETCDF
694  } else if (plotfile_type == PlotFileType::Netcdf) {
695  int lev = 0;
696  int l_which = 0;
697  const Real* p_lo = my_geom[lev].ProbLo();
698  const Real* p_hi = my_geom[lev].ProbHi();
699  const auto dx = my_geom[lev].CellSize();
700  writeNCPlotFile(lev, l_which, plotfilename, GetVecOfConstPtrs(mf), varnames, istep,
701  {p_lo[0],p_lo[1],p_lo[2]},{p_hi[0],p_hi[1],dx[2]}, {dx[0],dx[1],dx[2]},
702  my_geom[lev].Domain(), static_cast<Real>(t_new[0]),
703  static_cast<Real>(start_bdy_time), solverChoice, zlevels_stag[lev]);
704 #endif
705  } else {
706  // Here we assume the plotfile_type is PlotFileType::None
707  Print() << "Writing no 2D plotfile since plotfile_type is none" << std::endl;
708  }
709 }
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 double &time, const double &start_bdy_time, const SolverChoice &solverChoice, const Vector< Real > &zlevels_stag)
Definition: ERF_NCPlotFile.cpp:20
void writeJobInfo(const std::string &dir, erf_provenance::ArtifactType artifact_type, int artifact_step, double artifact_time_seconds) const
Definition: ERF_WriteJobInfo.cpp:15
void write_2d_metadata_json(const std::string &plotfilename, const amrex::Vector< std::string > &varnames)
Definition: ERF_Plotfile2DMetadata.cpp:232
amrex::Vector< Plotfile2DOutputDescriptor > build_sampled_level_output_descriptors(const std::string &pp_prefix, int which, const amrex::Vector< std::string > &static_plot_vars, const SolverChoice &solver_choice)
Definition: ERF_Plotfile2DSampledLevel.cpp:492
Here is the call graph for this function:

◆ Write3DPlotFile()

bool ERF::Write3DPlotFile ( int  which,
PlotFileType  plotfile_type,
amrex::Vector< std::string >  plot_var_names 
)
2069 {
2070  auto dPlotTime0 = amrex::second();
2071 
2072  Real tnew = static_cast<Real>(t_new[0]);
2073 
2074  const Vector<std::string> varnames = PlotFileVarNames(plot_var_names);
2075  const int ncomp_mf = static_cast<int>(varnames.size());
2076 
2077  if (ncomp_mf == 0) return false;
2078 
2079  Plot3DScratch scratch;
2080  BuildPlot3DScratch(plot_var_names, scratch);
2081 
2082  // Vector of MultiFabs for cell-centered data
2083  Vector<MultiFab> mf(finest_level+1);
2084  for (int lev = 0; lev <= finest_level; ++lev) {
2085  mf[lev].define(grids[lev], dmap[lev], ncomp_mf, 0);
2086  }
2087 
2088  // Vector of MultiFabs for nodal data
2089  Vector<MultiFab> mf_nd(finest_level+1);
2090  if ( SolverChoice::mesh_type != MeshType::ConstantDz) {
2091  for (int lev = 0; lev <= finest_level; ++lev) {
2092  BoxArray nodal_grids(grids[lev]); nodal_grids.surroundingNodes();
2093  mf_nd[lev].define(nodal_grids, dmap[lev], 3, 0);
2094  mf_nd[lev].setVal(0.);
2095  }
2096  }
2097 
2098  // Vector of MultiFabs for face-centered velocity
2099  Vector<MultiFab> mf_u(finest_level+1);
2100  Vector<MultiFab> mf_v(finest_level+1);
2101  Vector<MultiFab> mf_w(finest_level+1);
2102  if (m_plot_face_vels) {
2103  for (int lev = 0; lev <= finest_level; ++lev) {
2104  BoxArray grid_stag_u(grids[lev]); grid_stag_u.surroundingNodes(0);
2105  BoxArray grid_stag_v(grids[lev]); grid_stag_v.surroundingNodes(1);
2106  BoxArray grid_stag_w(grids[lev]); grid_stag_w.surroundingNodes(2);
2107  mf_u[lev].define(grid_stag_u, dmap[lev], 1, 0);
2108  mf_v[lev].define(grid_stag_v, dmap[lev], 1, 0);
2109  mf_w[lev].define(grid_stag_w, dmap[lev], 1, 0);
2110  MultiFab::Copy(mf_u[lev],vars_new[lev][Vars::xvel],0,0,1,0);
2111  MultiFab::Copy(mf_v[lev],vars_new[lev][Vars::yvel],0,0,1,0);
2112  MultiFab::Copy(mf_w[lev],vars_new[lev][Vars::zvel],0,0,1,0);
2113  }
2114  }
2115 
2116  for (int lev = 0; lev <= finest_level; ++lev) {
2117  FillPlot3DVars(lev, plot_var_names, scratch, mf[lev], ncomp_mf, tnew);
2118  }
2119 
2120  if (solverChoice.terrain_type == TerrainType::EB)
2121  {
2122  for (int lev = 0; lev <= finest_level; ++lev) {
2123  EB_set_covered(mf[lev], zero);
2124  }
2125  }
2126 
2127  // Fill terrain distortion MF (nu_nd)
2128  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
2129  for (int lev(0); lev <= finest_level; ++lev) {
2130  MultiFab::Copy(mf_nd[lev],*z_phys_nd[lev],0,2,1,0);
2131  Real dz = Geom()[lev].CellSizeArray()[2];
2132  for (MFIter mfi(mf_nd[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
2133  const Box& bx = mfi.tilebox();
2134  Array4<Real> mf_arr = mf_nd[lev].array(mfi);
2135  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
2136  {
2137  mf_arr(i,j,k,2) -= k * dz;
2138  });
2139  }
2140  }
2141  }
2142 
2143  std::string plotfilename;
2144  std::string plotfilenameU;
2145  std::string plotfilenameV;
2146  std::string plotfilenameW;
2147 
2148  if (which == 1) {
2150  const std::string dt_format = "%Y-%m-%d_%H:%M:%S"; // ISO 8601 standard
2151  plotfilename = plot3d_file_1+"_"+getTimestamp(start_time+tnew, dt_format,false);
2152  } else {
2153  plotfilename = Concatenate(plot3d_file_1, istep[0], file_name_digits);
2154  }
2155  plotfilenameU = Concatenate(plot3d_file_1+"U", istep[0], file_name_digits);
2156  plotfilenameV = Concatenate(plot3d_file_1+"V", istep[0], file_name_digits);
2157  plotfilenameW = Concatenate(plot3d_file_1+"W", istep[0], file_name_digits);
2158  } else if (which == 2) {
2160  const std::string dt_format = "%Y-%m-%d_%H:%M:%S"; // ISO 8601 standard
2161  plotfilename = plot3d_file_2+"_"+getTimestamp(start_time+tnew, dt_format,false);
2162  } else {
2163  plotfilename = Concatenate(plot3d_file_2, istep[0], file_name_digits);
2164  }
2165  plotfilenameU = Concatenate(plot3d_file_2+"U", istep[0], file_name_digits);
2166  plotfilenameV = Concatenate(plot3d_file_2+"V", istep[0], file_name_digits);
2167  plotfilenameW = Concatenate(plot3d_file_2+"W", istep[0], file_name_digits);
2168  }
2169 
2170  // LSM writes it's own data
2171  if (which==1 && plot_lsm) {
2172  lsm.Plot_Lsm_Data(tnew, finest_level, istep, refRatio());
2173  }
2174 
2175 #ifdef ERF_USE_RRTMGP
2176  /*
2177  // write additional RRTMGP data
2178  // TODO: currently single level only
2179  if (which==1 && plot_rad) {
2180  rad[0]->writePlotfile(plot_file_1, tnew, istep[0]);
2181  }
2182  */
2183 #endif
2184 
2185  // Single level
2186  if (finest_level == 0)
2187  {
2188  if (plotfile_type == PlotFileType::Amrex)
2189  {
2190  Print() << "Writing native 3D plotfile " << plotfilename << "\n";
2191  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
2192  WriteMultiLevelPlotfileWithTerrain(plotfilename, finest_level+1,
2193  GetVecOfConstPtrs(mf),
2194  GetVecOfConstPtrs(mf_nd),
2195  varnames,
2196  Geom(), tnew, istep, refRatio());
2197  } else {
2198  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
2199  GetVecOfConstPtrs(mf),
2200  varnames,
2201  Geom(), tnew, istep, refRatio());
2202  }
2204  istep[0], t_new[0]);
2205 
2206  if (m_plot_face_vels) {
2207  Print() << "Writing face velocities" << std::endl;
2208  WriteMultiLevelPlotfile(plotfilenameU, finest_level+1,
2209  GetVecOfConstPtrs(mf_u),
2210  {"x_velocity_stag"},
2211  Geom(), tnew, istep, refRatio());
2212  WriteMultiLevelPlotfile(plotfilenameV, finest_level+1,
2213  GetVecOfConstPtrs(mf_v),
2214  {"y_velocity_stag"},
2215  Geom(), tnew, istep, refRatio());
2216  WriteMultiLevelPlotfile(plotfilenameW, finest_level+1,
2217  GetVecOfConstPtrs(mf_w),
2218  {"z_velocity_stag"},
2219  Geom(), tnew, istep, refRatio());
2220  }
2221 
2223  (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
2224  solverChoice.buildings_type == BuildingsType::ImmersedForcing) &&
2225  terrain_blanking_xface[0]) { // Check if face arrays are allocated
2226  Print() << "Writing face terrain blanking" << std::endl;
2227  std::string plotfilenameTBX = plotfilename; plotfilenameTBX += "_terrain_blank_xface";
2228  std::string plotfilenameTBY = plotfilename; plotfilenameTBY += "_terrain_blank_yface";
2229  std::string plotfilenameTBZ = plotfilename; plotfilenameTBZ += "_terrain_blank_zface";
2230  WriteMultiLevelPlotfile(plotfilenameTBX, finest_level+1,
2231  GetVecOfConstPtrs(terrain_blanking_xface),
2232  {"terrain_blank_xface"},
2233  Geom(), tnew, istep, refRatio());
2234  WriteMultiLevelPlotfile(plotfilenameTBY, finest_level+1,
2235  GetVecOfConstPtrs(terrain_blanking_yface),
2236  {"terrain_blank_yface"},
2237  Geom(), tnew, istep, refRatio());
2238  WriteMultiLevelPlotfile(plotfilenameTBZ, finest_level+1,
2239  GetVecOfConstPtrs(terrain_blanking_zface),
2240  {"terrain_blank_zface"},
2241  Geom(), tnew, istep, refRatio());
2242  }
2243 
2244 #ifdef ERF_USE_PARTICLES
2245  particleData.writePlotFile(plotfilename, z_phys_nd);
2246 #endif
2247 #ifdef ERF_USE_NETCDF
2248  } else if (plotfile_type == PlotFileType::Netcdf) {
2249  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
2250  int lev = 0;
2251  int l_which = 0;
2252  const Real* p_lo = geom[lev].ProbLo();
2253  const Real* p_hi = geom[lev].ProbHi();
2254  const auto dx = geom[lev].CellSize();
2255  writeNCPlotFile(lev, l_which, plotfilename, GetVecOfConstPtrs(mf), varnames, istep,
2256  {p_lo[0],p_lo[1],p_lo[2]},{p_hi[0],p_hi[1],p_hi[2]}, {dx[0],dx[1],dx[2]},
2257  geom[lev].Domain(), tnew, start_bdy_time, solverChoice, zlevels_stag[lev]);
2258 #endif
2259  } else {
2260  // Here we assume the plotfile_type is PlotFileType::None
2261  Print() << "Writing no 3D plotfile since plotfile_type is none" << std::endl;
2262  }
2263 
2264  } else { // Multilevel
2265 
2266  if (plotfile_type == PlotFileType::Amrex) {
2267 
2268  int lev0 = 0;
2269  int desired_ratio = std::max(std::max(ref_ratio[lev0][0],ref_ratio[lev0][1]),ref_ratio[lev0][2]);
2270  bool any_ratio_one = ( ( (ref_ratio[lev0][0] == 1) || (ref_ratio[lev0][1] == 1) ) ||
2271  (ref_ratio[lev0][2] == 1) );
2272  for (int lev = 1; lev < finest_level; lev++) {
2273  any_ratio_one = any_ratio_one ||
2274  ( ( (ref_ratio[lev][0] == 1) || (ref_ratio[lev][1] == 1) ) ||
2275  (ref_ratio[lev][2] == 1) );
2276  }
2277 
2278  if (any_ratio_one && m_expand_plotvars_to_unif_rr)
2279  {
2280  Vector<IntVect> r2(finest_level);
2281  Vector<Geometry> g2(finest_level+1);
2282  Vector<MultiFab> mf2(finest_level+1);
2283 
2284  mf2[0].define(grids[0], dmap[0], ncomp_mf, 0);
2285 
2286  // Copy level 0 as is
2287  MultiFab::Copy(mf2[0],mf[0],0,0,mf[0].nComp(),0);
2288 
2289  // Define a new multi-level array of Geometry's so that we pass the new "domain" at lev > 0
2290  Array<int,AMREX_SPACEDIM> periodicity =
2291  {Geom()[lev0].isPeriodic(0),Geom()[lev0].isPeriodic(1),Geom()[lev0].isPeriodic(2)};
2292  g2[lev0].define(Geom()[lev0].Domain(),&(Geom()[lev0].ProbDomain()),0,periodicity.data());
2293 
2294  r2[0] = IntVect(desired_ratio/ref_ratio[lev0][0],
2295  desired_ratio/ref_ratio[lev0][1],
2296  desired_ratio/ref_ratio[lev0][2]);
2297 
2298  for (int lev = 1; lev <= finest_level; ++lev) {
2299  if (lev > 1) {
2300  r2[lev-1][0] = r2[lev-2][0] * desired_ratio / ref_ratio[lev-1][0];
2301  r2[lev-1][1] = r2[lev-2][1] * desired_ratio / ref_ratio[lev-1][1];
2302  r2[lev-1][2] = r2[lev-2][2] * desired_ratio / ref_ratio[lev-1][2];
2303  }
2304 
2305  mf2[lev].define(refine(grids[lev],r2[lev-1]), dmap[lev], ncomp_mf, 0);
2306 
2307  // Set the new problem domain
2308  Box d2(Geom()[lev].Domain());
2309  d2.refine(r2[lev-1]);
2310 
2311  g2[lev].define(d2,&(Geom()[lev].ProbDomain()),0,periodicity.data());
2312  }
2313 
2314  //
2315  // We need to make a temporary that is the size of ncomp_mf
2316  // in order to not get an out of bounds error
2317  // even though the values will not be used
2318  //
2319  Vector<BCRec> temp_domain_bcs_type;
2320  temp_domain_bcs_type.resize(ncomp_mf);
2321 
2322  //
2323  // Do piecewise constant interpolation of mf into mf2
2324  //
2325  for (int lev = 1; lev <= finest_level; ++lev) {
2326  Interpolater* mapper_c = &pc_interp;
2327  InterpFromCoarseLevel(mf2[lev], static_cast<Real>(t_new[lev]), mf[lev],
2328  0, 0, ncomp_mf,
2329  geom[lev], g2[lev],
2331  r2[lev-1], mapper_c, temp_domain_bcs_type, 0);
2332  }
2333 
2334  // Define an effective ref_ratio which is isotropic to be passed into WriteMultiLevelPlotfile
2335  Vector<IntVect> rr(finest_level);
2336  for (int lev = 0; lev < finest_level; ++lev) {
2337  rr[lev] = IntVect(desired_ratio);
2338  }
2339 
2340  Print() << "Writing 3D plotfile " << plotfilename << "\n";
2341  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
2342  WriteMultiLevelPlotfileWithTerrain(plotfilename, finest_level+1,
2343  GetVecOfConstPtrs(mf2),
2344  GetVecOfConstPtrs(mf_nd),
2345  varnames,
2346  g2, tnew, istep, rr);
2347  } else {
2348  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
2349  GetVecOfConstPtrs(mf2), varnames,
2350  g2, tnew, istep, rr);
2351  }
2352 
2353  } else {
2354  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
2355  WriteMultiLevelPlotfileWithTerrain(plotfilename, finest_level+1,
2356  GetVecOfConstPtrs(mf),
2357  GetVecOfConstPtrs(mf_nd),
2358  varnames,
2359  geom, tnew, istep, ref_ratio);
2360  } else {
2361  WriteMultiLevelPlotfile(plotfilename, finest_level+1,
2362  GetVecOfConstPtrs(mf), varnames,
2363  geom, tnew, istep, ref_ratio);
2364  }
2365  if (m_plot_face_vels) {
2366  Print() << "Writing face velocities" << std::endl;
2367  WriteMultiLevelPlotfile(plotfilenameU, finest_level+1,
2368  GetVecOfConstPtrs(mf_u),
2369  {"x_velocity_stag"},
2370  geom, tnew, istep, ref_ratio);
2371  WriteMultiLevelPlotfile(plotfilenameV, finest_level+1,
2372  GetVecOfConstPtrs(mf_v),
2373  {"y_velocity_stag"},
2374  geom, tnew, istep, ref_ratio);
2375  WriteMultiLevelPlotfile(plotfilenameW, finest_level+1,
2376  GetVecOfConstPtrs(mf_w),
2377  {"z_velocity_stag"},
2378  geom, tnew, istep, ref_ratio);
2379  }
2380  } // ref_ratio test
2381 
2383  istep[0], t_new[0]);
2384 
2385 #ifdef ERF_USE_PARTICLES
2386  particleData.writePlotFile(plotfilename, z_phys_nd);
2387 #endif
2388 
2389 #ifdef ERF_USE_NETCDF
2390  } else if (plotfile_type == PlotFileType::Netcdf) {
2391  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
2392  for (int lev = 0; lev <= finest_level; ++lev) {
2393  for (int which_box = 0; which_box < num_boxes_at_level[lev]; which_box++) {
2394  Box bounding_region = (lev == 0) ? geom[lev].Domain() : boxes_at_level[lev][which_box];
2395  const Real* p_lo = geom[lev].ProbLo();
2396  const Real* p_hi = geom[lev].ProbHi();
2397  const auto dx = geom[lev].CellSizeArray();
2398  writeNCPlotFile(lev, which_box, plotfilename, GetVecOfConstPtrs(mf), varnames, istep,
2399  {p_lo[0],p_lo[1],p_lo[2]},{p_hi[0],p_hi[1],p_hi[2]}, {dx[0],dx[1],dx[2]},
2400  bounding_region, tnew, start_bdy_time, solverChoice, zlevels_stag[lev]);
2401  }
2402  }
2403 #endif
2404  }
2405  } // end multi-level
2406 
2407  if (verbose > 0)
2408  {
2409  auto dPlotTime = amrex::second() - dPlotTime0;
2410  ParallelDescriptor::ReduceRealMax(dPlotTime,ParallelDescriptor::IOProcessorNumber());
2411  amrex::Print() << "3DPlotfile write time = " << dPlotTime << " seconds." << '\n';
2412  }
2413 
2414  return true;
2415 
2416 }
static PhysBCFunctNoOp null_bc_for_fill
Definition: ERF_Plotfile.cpp:16
static amrex::Vector< std::string > PlotFileVarNames(amrex::Vector< std::string > plot_var_names)
Definition: ERF_Plotfile.cpp:276
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, double 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:2419
void Plot_Lsm_Data(double time, int finest_level, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &ref_ratio)
Definition: ERF_LandSurface.H:211
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
22 {
23  BL_PROFILE("ERF::write_1D_profiles()");
24 
25  if (NumDataLogs() > 1)
26  {
27  // Define the 1d arrays we will need
28  Gpu::HostVector<Real> h_avg_u, h_avg_v, h_avg_w;
29  Gpu::HostVector<Real> h_avg_rho, h_avg_th, h_avg_ksgs, h_avg_Kmv, h_avg_Khv;
30  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;
31  Gpu::HostVector<Real> h_avg_wthv;
32  Gpu::HostVector<Real> h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth;
33  Gpu::HostVector<Real> h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww;
34  Gpu::HostVector<Real> h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw;
35  Gpu::HostVector<Real> h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw;
36  Gpu::HostVector<Real> h_avg_tau11, h_avg_tau12, h_avg_tau13, h_avg_tau22, h_avg_tau23, h_avg_tau33;
37  Gpu::HostVector<Real> h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx, h_avg_sgsdiss; // only output tau_{theta,w} and epsilon for now
38 
39  Gpu::HostVector<Real> h_avg_ttend, h_avg_qtend, h_avg_wsub, h_avg_tnudge, h_avg_qnudge, h_avg_unudge, h_avg_vnudge;
40  Gpu::HostVector<Real> h_avg_thtend, h_avg_qhtend, h_avg_tvtend, h_avg_qvtend, h_avg_qcvtend;
41  if (NumDataLogs() > 1) {
43  h_avg_u, h_avg_v, h_avg_w,
44  h_avg_rho, h_avg_th, h_avg_ksgs,
45  h_avg_Kmv, h_avg_Khv,
46  h_avg_qv, h_avg_qc, h_avg_qr,
47  h_avg_wqv, h_avg_wqc, h_avg_wqr,
48  h_avg_qi, h_avg_qs, h_avg_qg,
49  h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww,
50  h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth,
51  h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw,
52  h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw,
53  h_avg_wthv);
54  }
55 
56  if (NumDataLogs() > 3 && time > zero) {
57  derive_stress_profiles(h_avg_tau11, h_avg_tau12, h_avg_tau13,
58  h_avg_tau22, h_avg_tau23, h_avg_tau33,
59  h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx,
60  h_avg_sgsdiss);
61  }
62 
63  if (NumDataLogs() > 4 && time > 0.) {
64  derive_forcing_profiles_stag(h_avg_ttend, h_avg_qtend, h_avg_wsub,
65  h_avg_thtend, h_avg_qhtend, h_avg_tvtend,
66  h_avg_qvtend, h_avg_qcvtend, h_avg_tnudge,
67  h_avg_qnudge, h_avg_unudge, h_avg_vnudge);
68  }
69 
70  int hu_size = h_avg_u.size();
71 
72  auto const& dx = geom[0].CellSizeArray();
73  if (ParallelDescriptor::IOProcessor()) {
74  if (NumDataLogs() > 1) {
75  std::ostream& data_log1 = DataLog(1);
76  if (data_log1.good()) {
77  // Write the quantities at this time
78  for (int k = 0; k < hu_size; k++) {
79  Real z;
80  if (zlevels_stag[0].size() > 1) {
81  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
82  } else {
83  z = (k + myhalf)* dx[2];
84  }
85  data_log1 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
86  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
87  << h_avg_u[k] << " " << h_avg_v[k] << " " << h_avg_w[k] << " "
88  << h_avg_rho[k] << " " << h_avg_th[k] << " " << h_avg_ksgs[k] << " "
89  << h_avg_Kmv[k] << " " << h_avg_Khv[k] << " "
90  << h_avg_qv[k] << " " << h_avg_qc[k] << " " << h_avg_qr[k] << " "
91  << h_avg_qi[k] << " " << h_avg_qs[k] << " " << h_avg_qg[k]
92  << std::endl;
93  } // loop over z
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 (int k = 0; k < hu_size; k++) {
102  Real z;
103  if (zlevels_stag[0].size() > 1) {
104  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
105  } else {
106  z = (k + myhalf)* dx[2];
107  }
108  Real thv = h_avg_th[k] * (one + epsv*h_avg_qv[k] - h_avg_qc[k] - h_avg_qr[k]);
109  data_log2 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
110  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
111  << h_avg_uu[k] - h_avg_u[k]*h_avg_u[k] << " "
112  << h_avg_uv[k] - h_avg_u[k]*h_avg_v[k] << " "
113  << h_avg_uw[k] - h_avg_u[k]*h_avg_w[k] << " "
114  << h_avg_vv[k] - h_avg_v[k]*h_avg_v[k] << " "
115  << h_avg_vw[k] - h_avg_v[k]*h_avg_w[k] << " "
116  << h_avg_ww[k] - h_avg_w[k]*h_avg_w[k] << " "
117  << h_avg_uth[k] - h_avg_u[k]*h_avg_th[k] << " "
118  << h_avg_vth[k] - h_avg_v[k]*h_avg_th[k] << " "
119  << h_avg_wth[k] - h_avg_w[k]*h_avg_th[k] << " "
120  << h_avg_thth[k] - h_avg_th[k]*h_avg_th[k] << " "
121  // Note: <u'_i u'_i u'_j> = <u_i u_i u_j>
122  // - <u_i u_i> * <u_j>
123  // - 2*<u_i> * <u_i u_j>
124  // + 2*<u_i>*<u_i> * <u_j>
125  << h_avg_uiuiu[k]
126  - (h_avg_uu[k] + h_avg_vv[k] + h_avg_ww[k])*h_avg_u[k]
127  - 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])
128  + 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]
129  << " " // (u'_i u'_i)u'
130  << h_avg_uiuiv[k]
131  - (h_avg_uu[k] + h_avg_vv[k] + h_avg_ww[k])*h_avg_v[k]
132  - 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])
133  + 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]
134  << " " // (u'_i u'_i)v'
135  << h_avg_uiuiw[k]
136  - (h_avg_uu[k] + h_avg_vv[k] + h_avg_ww[k])*h_avg_w[k]
137  - 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])
138  + 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]
139  << " " // (u'_i u'_i)w'
140  << h_avg_pu[k] - h_avg_p[k]*h_avg_u[k] << " "
141  << h_avg_pv[k] - h_avg_p[k]*h_avg_v[k] << " "
142  << h_avg_pw[k] - h_avg_p[k]*h_avg_w[k] << " "
143  << h_avg_wqv[k] - h_avg_qv[k]*h_avg_w[k] << " "
144  << h_avg_wqc[k] - h_avg_qc[k]*h_avg_w[k] << " "
145  << h_avg_wqr[k] - h_avg_qr[k]*h_avg_w[k] << " "
146  << h_avg_wthv[k] - h_avg_w[k]*thv
147  << std::endl;
148  } // loop over z
149  } // if good
150  } // NumDataLogs
151 
152  if (NumDataLogs() > 3 && time > zero) {
153  std::ostream& data_log3 = DataLog(3);
154  if (data_log3.good()) {
155  // Write the average stresses
156  for (int k = 0; k < hu_size; k++) {
157  Real z;
158  if (zlevels_stag[0].size() > 1) {
159  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
160  } else {
161  z = (k + myhalf)* dx[2];
162  }
163  data_log3 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
164  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
165  << h_avg_tau11[k] << " " << h_avg_tau12[k] << " " << h_avg_tau13[k] << " "
166  << h_avg_tau22[k] << " " << h_avg_tau23[k] << " " << h_avg_tau33[k] << " "
167  << h_avg_sgshfx[k] << " "
168  << h_avg_sgsq1fx[k] << " " << h_avg_sgsq2fx[k] << " "
169  << h_avg_sgsdiss[k]
170  << std::endl;
171  } // loop over z
172  } // if good
173  } // if (NumDataLogs() > 3)
174 
175  if (NumDataLogs() > 4 && time > 0.) {
176  std::ostream& data_log4 = DataLog(4);
177  if (data_log4.good()) {
178  for (int k = 0; k < hu_size; k++) {
179  Real z;
180  if (zlevels_stag[0].size() > 1) {
181  z = myhalf * (zlevels_stag[0][k] + zlevels_stag[0][k+1]);
182  } else {
183  z = (k + myhalf)* dx[2];
184  }
185  data_log4 << std::setw(datwidth) << std::setprecision(timeprecision) << time << " "
186  << std::setw(datwidth) << std::setprecision(datprecision) << z << " "
187  << h_avg_ttend[k] << " " << h_avg_qtend[k] << " " << h_avg_wsub[k] << " "
188  << h_avg_thtend[k] << " " << h_avg_qhtend[k] << " " << h_avg_tvtend[k] << " "
189  << h_avg_qvtend[k] << " " << h_avg_qcvtend[k] << " " << h_avg_tnudge[k] << " "
190  << h_avg_qnudge[k] << " " << h_avg_unudge[k] << " " << h_avg_vnudge[k]
191  << std::endl;
192  } // loop over z
193  }
194  }
195  } // if IOProcessor
196  } // if (NumDataLogs() > 1)
197 }
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:241
void derive_forcing_profiles_stag(amrex::Gpu::HostVector< amrex::Real > &h_avg_ttend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_wsub, amrex::Gpu::HostVector< amrex::Real > &h_avg_thtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qhtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_tvtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qvtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_qcvtend, amrex::Gpu::HostVector< amrex::Real > &h_avg_tnudge, amrex::Gpu::HostVector< amrex::Real > &h_avg_qnudge, amrex::Gpu::HostVector< amrex::Real > &h_avg_unudge, amrex::Gpu::HostVector< amrex::Real > &h_avg_vnudge)
Definition: ERF_Write1DProfiles_stag.cpp:803
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:543

◆ 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
30 {
31  BL_PROFILE("ERF::write_1D_profiles_stag()");
32 
33  int prof_datwidth = 14;
34  int prof_datprecision = 9;
35  int prof_timeprecision = 13; // e.g., 1-yr LES: 31,536,000 s with dt ~ 0.01 ==> min prec = 10
36 
37 
38  if (NumDataLogs() > 1)
39  {
40  // Define the 1d arrays we will need
41  Gpu::HostVector<Real> h_avg_u, h_avg_v, h_avg_w;
42  Gpu::HostVector<Real> h_avg_rho, h_avg_th, h_avg_ksgs, h_avg_Kmv, h_avg_Khv;
43  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;
44  Gpu::HostVector<Real> h_avg_wthv;
45  Gpu::HostVector<Real> h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth;
46  Gpu::HostVector<Real> h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww;
47  Gpu::HostVector<Real> h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw;
48  Gpu::HostVector<Real> h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw;
49  Gpu::HostVector<Real> h_avg_tau11, h_avg_tau12, h_avg_tau13, h_avg_tau22, h_avg_tau23, h_avg_tau33;
50  Gpu::HostVector<Real> h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx, h_avg_sgsdiss; // only output tau_{theta,w} and epsilon for now
51 
52 
53  Gpu::HostVector<Real> h_avg_ttend, h_avg_qtend, h_avg_wsub, h_avg_tnudge, h_avg_qnudge, h_avg_unudge, h_avg_vnudge;
54  Gpu::HostVector<Real> h_avg_thtend, h_avg_qhtend, h_avg_tvtend, h_avg_qvtend, h_avg_qcvtend;
55  {
57  h_avg_u, h_avg_v, h_avg_w,
58  h_avg_rho, h_avg_th, h_avg_ksgs,
59  h_avg_Kmv, h_avg_Khv,
60  h_avg_qv, h_avg_qc, h_avg_qr,
61  h_avg_wqv, h_avg_wqc, h_avg_wqr,
62  h_avg_qi, h_avg_qs, h_avg_qg,
63  h_avg_uu, h_avg_uv, h_avg_uw, h_avg_vv, h_avg_vw, h_avg_ww,
64  h_avg_uth, h_avg_vth, h_avg_wth, h_avg_thth,
65  h_avg_uiuiu, h_avg_uiuiv, h_avg_uiuiw,
66  h_avg_p, h_avg_pu, h_avg_pv, h_avg_pw,
67  h_avg_wthv);
68  }
69 
70  if (NumDataLogs() > 3 && time > zero) {
71  derive_stress_profiles_stag(h_avg_tau11, h_avg_tau12, h_avg_tau13,
72  h_avg_tau22, h_avg_tau23, h_avg_tau33,
73  h_avg_sgshfx, h_avg_sgsq1fx, h_avg_sgsq2fx,
74  h_avg_sgsdiss);
75  }
76 
77  if (NumDataLogs() > 4 && time > 0.) {
78  derive_forcing_profiles_stag(h_avg_ttend, h_avg_qtend, h_avg_wsub,
79  h_avg_thtend, h_avg_qhtend, h_avg_tvtend,
80  h_avg_qvtend, h_avg_qcvtend, h_avg_tnudge,
81  h_avg_qnudge, h_avg_unudge, h_avg_vnudge);
82  }
83 
84  int unstag_size = h_avg_w.size() - 1; // _un_staggered heights
85 
86  auto const& dx = geom[0].CellSizeArray();
87  if (ParallelDescriptor::IOProcessor()) {
88  if (NumDataLogs() > 1) {
89  std::ostream& data_log1 = DataLog(1);
90  if (data_log1.good()) {
91  // Write the quantities at this time
92  for (int k = 0; k < unstag_size; k++) {
93  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
94  data_log1 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
95  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
96  << h_avg_u[k] << " " << h_avg_v[k] << " " << h_avg_w[k] << " "
97  << h_avg_rho[k] << " " << h_avg_th[k] << " " << h_avg_ksgs[k] << " "
98  << h_avg_Kmv[k] << " " << h_avg_Khv[k] << " "
99  << h_avg_qv[k] << " " << h_avg_qc[k] << " " << h_avg_qr[k] << " "
100  << h_avg_qi[k] << " " << h_avg_qs[k] << " " << h_avg_qg[k]
101  << std::endl;
102  } // loop over z
103  // Write top face values
104  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
105  data_log1 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
106  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
107  << 0 << " " << 0 << " " << h_avg_w[unstag_size] << " "
108  << 0 << " " << 0 << " " << 0 << " " // rho, theta, ksgs
109  << 0 << " " << 0 << " " // Kmv, Khv
110  << 0 << " " << 0 << " " << 0 << " " // qv, qc, qr
111  << 0 << " " << 0 << " " << 0 // qi, qs, qg
112  << std::endl;
113  } // if good
114  } // NumDataLogs
115 
116  if (NumDataLogs() > 2) {
117  std::ostream& data_log2 = DataLog(2);
118  if (data_log2.good()) {
119  // Write the perturbational quantities at this time
120  // For surface values (k=0), assume w = uw = vw = ww = 0
121  Real w_cc = h_avg_w[1] / 2; // w at first cell center
122  Real uw_cc = h_avg_uw[1] / 2; // u*w at first cell center
123  Real vw_cc = h_avg_vw[1] / 2; // v*w at first cell center
124  Real ww_cc = h_avg_ww[1] / 2; // w*w at first cell center
125  data_log2 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
126  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << 0 << " "
127  << h_avg_uu[0] - h_avg_u[0]*h_avg_u[0] << " " // u'u'
128  << h_avg_uv[0] - h_avg_u[0]*h_avg_v[0] << " " // u'v'
129  << 0 << " " // u'w'
130  << h_avg_vv[0] - h_avg_v[0]*h_avg_v[0] << " " // v'v'
131  << 0 << " " // v'w'
132  << 0 << " " // w'w'
133  << h_avg_uth[0] - h_avg_u[0]*h_avg_th[0] << " " // u'th'
134  << h_avg_vth[0] - h_avg_v[0]*h_avg_th[0] << " " // v'th'
135  << 0 << " " // w'th'
136  << h_avg_thth[0] - h_avg_th[0]*h_avg_th[0] << " " // th'th'
137  << h_avg_uiuiu[0]
138  - (h_avg_uu[0] + h_avg_vv[0] + ww_cc)*h_avg_u[0]
139  - 2*(h_avg_u[0]*h_avg_uu[0] + h_avg_v[0]*h_avg_uv[0] + w_cc*uw_cc)
140  + 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]
141  << " " // (u'_i u'_i)u'
142  << h_avg_uiuiv[0]
143  - (h_avg_uu[0] + h_avg_vv[0] + ww_cc)*h_avg_v[0]
144  - 2*(h_avg_u[0]*h_avg_uv[0] + h_avg_v[0]*h_avg_vv[0] + w_cc*vw_cc)
145  + 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]
146  << " " // (u'_i u'_i)v'
147  << 0 << " " // (u'_i u'_i)w'
148  << h_avg_pu[0] - h_avg_p[0]*h_avg_u[0] << " " // p'u'
149  << h_avg_pv[0] - h_avg_p[0]*h_avg_v[0] << " " // p'v'
150  << 0 << " " // p'w'
151  << 0 << " " // qv'w'
152  << 0 << " " // qc'w'
153  << 0 << " " // qr'w'
154  << 0 // thv'w'
155  << std::endl;
156 
157  // For internal values, interpolate scalar quantities to faces
158  for (int k = 1; k < unstag_size; k++) {
159  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
160  Real uface = myhalf*(h_avg_u[k] + h_avg_u[k-1]);
161  Real vface = myhalf*(h_avg_v[k] + h_avg_v[k-1]);
162  Real thface = myhalf*(h_avg_th[k] + h_avg_th[k-1]);
163  Real pface = myhalf*(h_avg_p[k] + h_avg_p[k-1]);
164  Real qvface = myhalf*(h_avg_qv[k] + h_avg_qv[k-1]);
165  Real qcface = myhalf*(h_avg_qc[k] + h_avg_qc[k-1]);
166  Real qrface = myhalf*(h_avg_qr[k] + h_avg_qr[k-1]);
167  Real uuface = myhalf*(h_avg_uu[k] + h_avg_uu[k-1]);
168  Real vvface = myhalf*(h_avg_vv[k] + h_avg_vv[k-1]);
169  Real thvface = thface * (one + epsv*qvface - qcface - qrface);
170  w_cc = myhalf*(h_avg_w[k-1] + h_avg_w[k]);
171  uw_cc = myhalf*(h_avg_uw[k-1] + h_avg_uw[k]);
172  vw_cc = myhalf*(h_avg_vw[k-1] + h_avg_vw[k]);
173  ww_cc = myhalf*(h_avg_ww[k-1] + h_avg_ww[k]);
174  data_log2 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
175  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
176  << h_avg_uu[k] - h_avg_u[k]*h_avg_u[k] << " " // u'u'
177  << h_avg_uv[k] - h_avg_u[k]*h_avg_v[k] << " " // u'v'
178  << h_avg_uw[k] - uface*h_avg_w[k] << " " // u'w'
179  << h_avg_vv[k] - h_avg_v[k]*h_avg_v[k] << " " // v'v'
180  << h_avg_vw[k] - vface*h_avg_w[k] << " " // v'w'
181  << h_avg_ww[k] - h_avg_w[k]*h_avg_w[k] << " " // w'w'
182  << h_avg_uth[k] - h_avg_u[k]*h_avg_th[k] << " " // u'th'
183  << h_avg_vth[k] - h_avg_v[k]*h_avg_th[k] << " " // v'th'
184  << h_avg_wth[k] - h_avg_w[k]*thface << " " // w'th'
185  << h_avg_thth[k] - h_avg_th[k]*h_avg_th[k] << " " // th'th'
186  // Note: <u'_i u'_i u'_j> = <u_i u_i u_j>
187  // - <u_i u_i> * <u_j>
188  // - 2*<u_i> * <u_i u_j>
189  // + 2*<u_i>*<u_i> * <u_j>
190  << h_avg_uiuiu[k]
191  - (h_avg_uu[k] + h_avg_vv[k] + ww_cc)*h_avg_u[k]
192  - 2*(h_avg_u[k]*h_avg_uu[k] + h_avg_v[k]*h_avg_uv[k] + w_cc*uw_cc)
193  + 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]
194  << " " // cell-centered (u'_i u'_i)u'
195  << h_avg_uiuiv[k]
196  - (h_avg_uu[k] + h_avg_vv[k] + ww_cc)*h_avg_v[k]
197  - 2*(h_avg_u[k]*h_avg_uv[k] + h_avg_v[k]*h_avg_vv[k] + w_cc*vw_cc)
198  + 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]
199  << " " // cell-centered (u'_i u'_i)v'
200  << h_avg_uiuiw[k]
201  - (uuface + vvface + h_avg_ww[k])*h_avg_w[k]
202  - 2*(uface*h_avg_uw[k] + vface*h_avg_vw[k] + h_avg_w[k]*h_avg_ww[k])
203  + 2*(uface*uface + vface*vface + h_avg_w[k]*h_avg_w[k])*h_avg_w[k]
204  << " " // face-centered (u'_i u'_i)w'
205  << h_avg_pu[k] - h_avg_p[k]*h_avg_u[k] << " " // cell-centered p'u'
206  << h_avg_pv[k] - h_avg_p[k]*h_avg_v[k] << " " // cell-centered p'v'
207  << h_avg_pw[k] - pface*h_avg_w[k] << " " // face-centered p'w'
208  << h_avg_wqv[k] - qvface*h_avg_w[k] << " "
209  << h_avg_wqc[k] - qcface*h_avg_w[k] << " "
210  << h_avg_wqr[k] - qrface*h_avg_w[k] << " "
211  << h_avg_wthv[k] - thvface*h_avg_w[k]
212  << std::endl;
213  } // loop over z
214 
215  // Write top face values, extrapolating scalar quantities
216  const int k = unstag_size;
217  Real uface = Real(1.5)*h_avg_u[k-1] - myhalf*h_avg_u[k-2];
218  Real vface = Real(1.5)*h_avg_v[k-1] - myhalf*h_avg_v[k-2];
219  Real thface = Real(1.5)*h_avg_th[k-1] - myhalf*h_avg_th[k-2];
220  Real pface = Real(1.5)*h_avg_p[k-1] - myhalf*h_avg_p[k-2];
221  Real qvface = Real(1.5)*h_avg_qv[k-1] - myhalf*h_avg_qv[k-2];
222  Real qcface = Real(1.5)*h_avg_qc[k-1] - myhalf*h_avg_qc[k-2];
223  Real qrface = Real(1.5)*h_avg_qr[k-1] - myhalf*h_avg_qr[k-2];
224  Real uuface = Real(1.5)*h_avg_uu[k-1] - myhalf*h_avg_uu[k-2];
225  Real vvface = Real(1.5)*h_avg_vv[k-1] - myhalf*h_avg_vv[k-2];
226  Real thvface = thface * (one + epsv*qvface - qcface - qrface);
227  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
228  data_log2 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
229  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
230  << 0 << " " // u'u'
231  << 0 << " " // u'v'
232  << h_avg_uw[k] - uface*h_avg_w[k] << " " // u'w'
233  << 0 << " " // v'v'
234  << h_avg_vw[k] - vface*h_avg_w[k] << " " // v'w'
235  << h_avg_ww[k] - h_avg_w[k]*h_avg_w[k] << " " // w'w'
236  << 0 << " " // u'th'
237  << 0 << " " // v'th'
238  << h_avg_wth[k] - thface*h_avg_w[k] << " " // w'th'
239  << 0 << " " // th'th'
240  << 0 << " " // (u'_i u'_i)u'
241  << 0 << " " // (u'_i u'_i)v'
242  << h_avg_uiuiw[k]
243  - (uuface + vvface + h_avg_ww[k])*h_avg_w[k]
244  - 2*(uface*h_avg_uw[k] + vface*h_avg_vw[k] + h_avg_w[k]*h_avg_ww[k])
245  + 2*(uface*uface + vface*vface + h_avg_w[k]*h_avg_w[k])*h_avg_w[k]
246  << " " // (u'_i u'_i)w'
247  << 0 << " " // pu'
248  << 0 << " " // pv'
249  << h_avg_pw[k] - pface*h_avg_w[k] << " " // pw'
250  << h_avg_wqv[k] - qvface*h_avg_w[k] << " "
251  << h_avg_wqc[k] - qcface*h_avg_w[k] << " "
252  << h_avg_wqr[k] - qrface*h_avg_w[k] << " "
253  << h_avg_wthv[k] - thvface*h_avg_w[k]
254  << std::endl;
255  } // if good
256  } // NumDataLogs
257 
258  if (NumDataLogs() > 3 && time > zero) {
259  std::ostream& data_log3 = DataLog(3);
260  if (data_log3.good()) {
261  // Write the average stresses
262  for (int k = 0; k < unstag_size; k++) {
263  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
264  data_log3 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
265  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
266  << h_avg_tau11[k] << " " << h_avg_tau12[k] << " " << h_avg_tau13[k] << " "
267  << h_avg_tau22[k] << " " << h_avg_tau23[k] << " " << h_avg_tau33[k] << " "
268  << h_avg_sgshfx[k] << " "
269  << h_avg_sgsq1fx[k] << " " << h_avg_sgsq2fx[k] << " "
270  << h_avg_sgsdiss[k]
271  << std::endl;
272  } // loop over z
273  // Write top face values
274  Real NANval = zero;
275  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
276  data_log3 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
277  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
278  << NANval << " " << NANval << " " << h_avg_tau13[unstag_size] << " "
279  << NANval << " " << h_avg_tau23[unstag_size] << " " << NANval << " "
280  << h_avg_sgshfx[unstag_size] << " "
281  << h_avg_sgsq1fx[unstag_size] << " " << h_avg_sgsq2fx[unstag_size] << " "
282  << NANval
283  << std::endl;
284  } // if good
285  } // if (NumDataLogs() > 3)
286 
287  if (NumDataLogs() > 4 && time > 0.) {
288  std::ostream& data_log4 = DataLog(4);
289  if (data_log4.good()) {
290  for (int k = 0; k < unstag_size; k++) {
291  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][k] : k * dx[2];
292  data_log4 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
293  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
294  << h_avg_ttend[k] << " " << h_avg_qtend[k] << " " << h_avg_wsub[k] << " "
295  << h_avg_thtend[k] << " " << h_avg_qhtend[k] << " " << h_avg_tvtend[k] << " "
296  << h_avg_qvtend[k] << " " << h_avg_qcvtend[k] << " " << h_avg_tnudge[k] << " "
297  << h_avg_qnudge[k] << " " << h_avg_unudge[k] << " " << h_avg_vnudge[k]
298  << std::endl;
299  } // loop over z
300  // Write top face values
301  Real NANval = zero;
302  Real z = (zlevels_stag[0].size() > 1) ? zlevels_stag[0][unstag_size] : unstag_size * dx[2];
303  data_log4 << std::setw(prof_datwidth) << std::setprecision(prof_timeprecision) << time << " "
304  << std::setw(prof_datwidth) << std::setprecision(prof_datprecision) << z << " "
305  << NANval << " " << NANval << " " << NANval << " "
306  << NANval << " " << NANval << " " << NANval << " "
307  << NANval << " " << NANval << " " << NANval << " "
308  << NANval << " " << NANval << " " << NANval
309  << std::endl;
310  }
311  }
312  } // if IOProcessor
313  } // if (NumDataLogs() > 1)
314 }
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:358
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:684

◆ WriteAtFinalTime()

void ERF::WriteAtFinalTime ( )
297 {
298  // Write plotfiles at final time
299  int plotfiles_3d_written = 0;
300  bool interval_diagnostic_consumed = false;
302  const bool wrote_plotfile = Write3DPlotFile(1,plotfile3d_type_1,plot3d_var_names_1);
303  if (wrote_plotfile) {
304  ++plotfiles_3d_written;
305  interval_diagnostic_consumed = interval_diagnostic_consumed ||
307  }
309  }
311  const bool wrote_plotfile = Write3DPlotFile(2,plotfile3d_type_2,plot3d_var_names_2);
312  if (wrote_plotfile) {
313  ++plotfiles_3d_written;
314  interval_diagnostic_consumed = interval_diagnostic_consumed ||
316  }
318  }
319  ResetIntervalMeansAfter3DPlotfileBatch(plotfiles_3d_written,
320  interval_diagnostic_consumed);
324  }
328  }
329 
330  for (int i = 0; i < m_subvol_int.size(); i++) {
331  if ( (m_subvol_int[i] > 0 || m_subvol_per[i] > zero) && istep[0] > last_subvol_step[i]) {
333  if (m_subvol_per[i] > zero) {last_subvol_time[i] += m_subvol_per[i];}
334  }
335  }
336 
337  if ( (m_check_int > 0 || m_check_per > zero) && istep[0] > last_check_file_step) {
340  }
341 }
Here is the call graph for this function:

◆ WriteAtIntermediateTime()

void ERF::WriteAtIntermediateTime ( int  nstep,
double  time 
)
240 {
241  int plotfiles_3d_written = 0;
242  bool interval_diagnostic_consumed = false;
243  if (writeNow(cur_time, step+1, m_plot3d_int_1, m_plot3d_per_1, dt[0], last_plot3d_file_time_1)) {
244  last_plot3d_file_step_1 = step+1;
245  const bool wrote_plotfile = Write3DPlotFile(1,plotfile3d_type_1,plot3d_var_names_1);
246  if (wrote_plotfile) {
247  ++plotfiles_3d_written;
248  interval_diagnostic_consumed = interval_diagnostic_consumed ||
250  }
251  for (int lev = 0; lev <= finest_level; ++lev) {lsm.Plot(lev, step+1);}
253  }
254  if (writeNow(cur_time, step+1, m_plot3d_int_2, m_plot3d_per_2, dt[0], last_plot3d_file_time_2)) {
255  last_plot3d_file_step_2 = step+1;
256  const bool wrote_plotfile = Write3DPlotFile(2,plotfile3d_type_2,plot3d_var_names_2);
257  if (wrote_plotfile) {
258  ++plotfiles_3d_written;
259  interval_diagnostic_consumed = interval_diagnostic_consumed ||
261  }
262  for (int lev = 0; lev <= finest_level; ++lev) {lsm.Plot(lev, step+1);}
264  }
265  ResetIntervalMeansAfter3DPlotfileBatch(plotfiles_3d_written,
266  interval_diagnostic_consumed);
267 
268  if (writeNow(cur_time, step+1, m_plot2d_int_1, m_plot2d_per_1, dt[0], last_plot2d_file_time_1)) {
269  last_plot2d_file_step_1 = step+1;
272  }
273 
274  if (writeNow(cur_time, step+1, m_plot2d_int_2, m_plot2d_per_2, dt[0], last_plot2d_file_time_2)) {
275  last_plot2d_file_step_2 = step+1;
278  }
279 
280  for (int i = 0; i < m_subvol_int.size(); i++) {
281  if (writeNow(cur_time, step+1, m_subvol_int[i], m_subvol_per[i], dt[0], last_subvol_time[i])) {
282  last_subvol_step[i] = step+1;
284  if (m_subvol_per[i] > zero) {last_subvol_time[i] += m_subvol_per[i];}
285  }
286  }
287 
288  if (writeNow(cur_time, step+1, m_check_int, m_check_per, dt[0], last_check_file_time)) {
289  last_check_file_step = step+1;
292  }
293 }
bool writeNow(double cur_time, const int nstep, const int plot_int, const double plot_per, const double dt_0, double &last_file_time)
Definition: ERF.cpp:3599
void Plot(const int &lev, const int &nstep)
Definition: ERF_LandSurface.H:90

Referenced by EvolveOneStep().

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

◆ writeBuildInfo()

void ERF::writeBuildInfo ( std::ostream &  os)
static
179 {
180  std::string PrettyLine = std::string(78, '=') + "\n";
181  std::string OtherLine = std::string(78, '-') + "\n";
182  std::string SkipSpace = std::string(8, ' ');
183 
184  // build information
185  os << PrettyLine;
186  os << " ERF Build Information\n";
187  os << PrettyLine;
188 
189  os << "build date: " << buildInfoGetBuildDate() << "\n";
190  os << "build machine: " << buildInfoGetBuildMachine() << "\n";
191  os << "build dir: " << buildInfoGetBuildDir() << "\n";
192  os << "AMReX dir: " << buildInfoGetAMReXDir() << "\n";
193 
194  os << "\n";
195 
196  os << "COMP: " << buildInfoGetComp() << "\n";
197  os << "COMP version: " << buildInfoGetCompVersion() << "\n";
198 
199  os << "C++ compiler: " << buildInfoGetCXXName() << "\n";
200  os << "C++ flags: " << buildInfoGetCXXFlags() << "\n";
201 
202  os << "\n";
203 
204  os << "Link flags: " << buildInfoGetLinkFlags() << "\n";
205  os << "Libraries: " << buildInfoGetLibraries() << "\n";
206 
207  os << "\n";
208 
209  for (int n = 1; n <= buildInfoGetNumModules(); n++) {
210  os << buildInfoGetModuleName(n) << ": "
211  << buildInfoGetModuleVal(n) << "\n";
212  }
213 
214  os << "\n";
215  const char* githash1 = buildInfoGetGitHash(1);
216  const char* githash2 = buildInfoGetGitHash(2);
217  if (strlen(githash1) > 0) {
218  os << "ERF git hash: " << githash1 << "\n";
219  }
220  if (strlen(githash2) > 0) {
221  os << "AMReX git hash: " << githash2 << "\n";
222  }
223 
224  const char* buildgithash = buildInfoGetBuildGitHash();
225  const char* buildgitname = buildInfoGetBuildGitName();
226  if (strlen(buildgithash) > 0) {
227  os << buildgitname << " git hash: " << buildgithash << "\n";
228  }
229 
230  os << "\n";
231  os << " ERF Compile time variables: \n";
232 
233  os << "\n";
234  os << " ERF Defines: \n";
235 #ifdef _OPENMP
236  os << std::setw(35) << std::left << "_OPENMP " << std::setw(6) << "ON"
237  << std::endl;
238 #else
239  os << std::setw(35) << std::left << "_OPENMP " << std::setw(6) << "OFF"
240  << std::endl;
241 #endif
242 
243 #ifdef MPI_VERSION
244  os << std::setw(35) << std::left << "MPI_VERSION " << std::setw(6)
245  << MPI_VERSION << std::endl;
246 #else
247  os << std::setw(35) << std::left << "MPI_VERSION " << std::setw(6)
248  << "UNDEFINED" << std::endl;
249 #endif
250 
251 #ifdef MPI_SUBVERSION
252  os << std::setw(35) << std::left << "MPI_SUBVERSION " << std::setw(6)
253  << MPI_SUBVERSION << std::endl;
254 #else
255  os << std::setw(35) << std::left << "MPI_SUBVERSION " << std::setw(6)
256  << "UNDEFINED" << std::endl;
257 #endif
258 
259  os << "\n\n";
260 }

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.

149 {
150  auto dCheckTime0 = amrex::second();
151 
152  // Station rows are buffered, and a restart from this checkpoint appends to
153  // the station files: without this the rows between the last flush and the
154  // checkpoint would be missing from the series the restart continues.
155  flush_stations();
156 
157  // chk00010 write a checkpoint file with this root directory
158  // chk00010/Header this contains information you need to save (e.g., finest_level, t_new, etc.) and also
159  // the BoxArrays at each level
160  // chk00010/Level_0/
161  // chk00010/Level_1/
162  // etc. these subdirectories will hold the MultiFab data at each level of refinement
163 
164  // checkpoint file name, e.g., chk00010
165  const std::string& checkpointname = Concatenate(check_file,istep[0],file_name_digits);
166 
167  Print() << "Writing native checkpoint " << checkpointname << "\n";
168 
169  const int nlevels = finest_level+1;
170 
171  // ---- prebuild a hierarchy of directories
172  // ---- dirName is built first. if dirName exists, it is renamed. then build
173  // ---- dirName/subDirPrefix_0 .. dirName/subDirPrefix_nlevels-1
174  // ---- if callBarrier is true, call ParallelDescriptor::Barrier()
175  // ---- after all directories are built
176  // ---- ParallelDescriptor::IOProcessor() creates the directories
177  PreBuildDirectorHierarchy(checkpointname, "Level_", nlevels, true);
178 
179  if (ParallelDescriptor::IOProcessor()) {
180  write_surface_temperature_contract(checkpointname);
181  }
182 
183  int ncomp_cons = vars_new[0][Vars::cons].nComp();
184 
185  // write Header file
186  if (ParallelDescriptor::IOProcessor()) {
187 
188  std::string HeaderFileName(checkpointname + "/Header");
189  VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
190  std::ofstream HeaderFile;
191  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
192  HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
193  std::ofstream::trunc |
194  std::ofstream::binary);
195  if(! HeaderFile.good()) {
196  FileOpenFailed(HeaderFileName);
197  }
198 
199  HeaderFile.precision(17);
200 
201  // write out title line
202  HeaderFile << "Checkpoint file for ERF\n";
203 
204  // write out finest_level
205  HeaderFile << finest_level << "\n";
206 
207  // write the number of components
208  // for each variable we store
209 
210  // conservative, cell-centered vars
211  HeaderFile << ncomp_cons << "\n";
212 
213  // x-velocity on faces
214  HeaderFile << 1 << "\n";
215 
216  // y-velocity on faces
217  HeaderFile << 1 << "\n";
218 
219  // z-velocity on faces
220  HeaderFile << 1 << "\n";
221 
222  // write out array of istep
223  for (int i = 0; i < istep.size(); ++i) {
224  HeaderFile << istep[i] << " ";
225  }
226  HeaderFile << "\n";
227 
228  // write out array of dt
229  for (int i = 0; i < dt.size(); ++i) {
230  HeaderFile << dt[i] << " ";
231  }
232  HeaderFile << "\n";
233 
234  // write out array of t_new
235  for (int i = 0; i < t_new.size(); ++i) {
236  HeaderFile << t_new[i] << " ";
237  }
238  HeaderFile << "\n";
239 
240  // write the BoxArray at each level
241  for (int lev = 0; lev <= finest_level; ++lev) {
242  boxArray(lev).writeOn(HeaderFile);
243  HeaderFile << '\n';
244  }
245 
246  // write out array of t_avg_cnt, the normalizer for the time-averaged velocity
247  //
248  // NOTE: this is written *after* the BoxArrays, i.e. last, on purpose. Nothing
249  // else is parsed out of the header past that point, so a reader that does
250  // not know about this line simply ignores it, and a reader that does can
251  // treat its absence (a checkpoint from before issue 3654 was fixed) as
252  // "restart the average", which is the behavior those files already had.
253  for (int i = 0; i < t_avg_cnt.size(); ++i) {
254  HeaderFile << t_avg_cnt[i] << " ";
255  }
256  HeaderFile << "\n";
257 
258  // Write separate file that tells how many components we have of the base state
259  std::string BaseStateFileName(checkpointname + "/num_base_state_comps");
260  std::ofstream BaseStateFile;
261  BaseStateFile.open(BaseStateFileName.c_str(), std::ofstream::out |
262  std::ofstream::trunc |
263  std::ofstream::binary);
264  if(! BaseStateFile.good()) {
265  FileOpenFailed(BaseStateFileName);
266  } else {
267  // write out number of components in base state
268  BaseStateFile << BaseState::num_comps << "\n";
269  BaseStateFile << base_state[0].nGrowVect() << "\n";
270  }
271 
272  // Persist the LSM scalar step counter (e.g. NoahMP itimestep). Without
273  // this, restart resets the substep schedule, which changes the LSM->MOST
274  // flux firing times and produces a non-bitwise trajectory vs. cold start.
275  if (solverChoice.lsm_type != LandSurfaceType::None) {
276  std::string LsmStepFileName(checkpointname + "/lsm_step");
277  std::ofstream LsmStepFile;
278  LsmStepFile.open(LsmStepFileName.c_str(), std::ofstream::out |
279  std::ofstream::trunc |
280  std::ofstream::binary);
281  if(! LsmStepFile.good()) {
282  FileOpenFailed(LsmStepFileName);
283  } else {
284  for (int lev = 0; lev <= finest_level; ++lev) {
285  LsmStepFile << lsm.Get_LSM_Step(lev) << " ";
286  }
287  LsmStepFile << "\n";
288  }
289  }
290 
291  // Persist the state of the exponential time filter used by the surface layer
292  // when erf.most.time_average is on. The plane and EB averaging policies hold
293  // that state in the plane averages below; every policy needs the per-level
294  // "is the filter initialized" flag, since restoring an average that was never
295  // filled would blend bogus_large_value into the next average. Without this
296  // file the filter simply starts over on restart, which is what checkpoints
297  // written before this was added already did.
298  int n_faces = 0;
299  for (OrientationIter oit; oit; ++oit) {
300  Orientation ori = oit();
301  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
302  n_faces += 1;
303  }
304  }
305  for (OrientationIter oit; oit; ++oit) {
306  Orientation ori = oit();
307  if (m_SurfaceLayer[ori] && m_SurfaceLayer[ori]->mac_avg_is_time_averaged()) {
308  std::string face = "_" + std::to_string(ori);
309  if (n_faces == 1 && static_cast<int>(ori) == Orientation::zlo()) {
310  face = "";
311  }
312 
313  std::string MostAvgFileName(checkpointname + "/most_time_average" + face);
314  std::ofstream MostAvgFile;
315  MostAvgFile.open(MostAvgFileName.c_str(), std::ofstream::out |
316  std::ofstream::trunc |
317  std::ofstream::binary);
318  if(! MostAvgFile.good()) {
319  FileOpenFailed(MostAvgFileName);
320  } else {
321  // Number of average components, then one line per level holding the
322  // initialization flag, how many plane averages follow (zero for the
323  // region policy, which keeps no plane averages), and those averages
324  MostAvgFile << m_SurfaceLayer[ori]->get_num_mac_avg() << "\n";
325  MostAvgFile.precision(17);
326  for (int lev = 0; lev <= finest_level; ++lev) {
327  const Vector<Real> pavg = m_SurfaceLayer[ori]->get_mac_plane_avg(lev);
328  MostAvgFile << (m_SurfaceLayer[ori]->mac_avg_is_initialized(lev) ? 1 : 0)
329  << " " << pavg.size();
330  for (int iavg(0); iavg < static_cast<int>(pavg.size()); ++iavg) {
331  MostAvgFile << " " << pavg[iavg];
332  }
333  MostAvgFile << "\n";
334  }
335  }
336  }
337  }
338  }
339 
340  // Interval means are stored separately from the legacy Header so older
341  // readers continue to parse that file unchanged. The metadata records
342  // the level/component layout; the v1 processor map is retained only for
343  // format compatibility and is not a restart constraint.
344  if (solverChoice.compute_mean_vars && ParallelDescriptor::IOProcessor()) {
345  const std::string metadata_name(checkpointname + "/IntervalMeansHeader");
346  std::ofstream metadata(metadata_name, std::ofstream::out |
347  std::ofstream::trunc |
348  std::ofstream::binary);
349  if (!metadata.good()) {
350  FileOpenFailed(metadata_name);
351  }
352  metadata.precision(17);
353  metadata << "ERF interval means checkpoint v1\n";
354  metadata << finest_level + 1 << " " << 10 << "\n";
355  for (int lev = 0; lev <= finest_level; ++lev) {
356  metadata << lev << " " << t_mean_cnt[lev] << " "
357  << mean_vars_time_reset_done << "\n";
358  boxArray(lev).writeOn(metadata);
359  metadata << '\n';
360  const auto& pmap = dmap[lev].ProcessorMap();
361  metadata << pmap.size();
362  for (const int proc : pmap) {
363  metadata << " " << proc;
364  }
365  metadata << '\n';
366  }
367  }
368 
369  // write the MultiFab data to, e.g., chk00010/Level_0/
370  // Here we make copies of the MultiFab with no ghost cells
371  for (int lev = 0; lev <= finest_level; ++lev)
372  {
373  MultiFab cons(grids[lev],dmap[lev],ncomp_cons,0);
374  MultiFab::Copy(cons,vars_new[lev][Vars::cons],0,0,ncomp_cons,0);
375  VisMF::Write(cons, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Cell"));
376 
377  MultiFab xvel(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
378  MultiFab::Copy(xvel,vars_new[lev][Vars::xvel],0,0,1,0);
379  VisMF::Write(xvel, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "XFace"));
380 
381  MultiFab yvel(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
382  MultiFab::Copy(yvel,vars_new[lev][Vars::yvel],0,0,1,0);
383  VisMF::Write(yvel, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "YFace"));
384 
385  MultiFab zvel(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
386  MultiFab::Copy(zvel,vars_new[lev][Vars::zvel],0,0,1,0);
387  VisMF::Write(zvel, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "ZFace"));
388 
389  if (solverChoice.anelastic[lev] == 1) {
390  MultiFab ppinc(grids[lev],dmap[lev],1,0);
391  MultiFab::Copy(ppinc,pp_inc[lev],0,0,1,0);
392  VisMF::Write(ppinc, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "PP_Inc"));
393 
394  MultiFab gpx(convert(grids[lev],IntVect(1,0,0)),dmap[lev],1,0);
395  MultiFab::Copy(gpx,gradp[lev][GpVars::gpx],0,0,1,0);
396  VisMF::Write(gpx, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Gpx"));
397 
398  MultiFab gpy(convert(grids[lev],IntVect(0,1,0)),dmap[lev],1,0);
399  MultiFab::Copy(gpy,gradp[lev][GpVars::gpy],0,0,1,0);
400  VisMF::Write(gpy, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Gpy"));
401 
402  MultiFab gpz(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
403  MultiFab::Copy(gpz,gradp[lev][GpVars::gpz],0,0,1,0);
404  VisMF::Write(gpz, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Gpz"));
405  }
406 
407  // Surface momentum and scalar fluxes handed to an ocean model in
408  // flux-passing mode. These are computed inside a timestep's RHS rather
409  // than carried as state, so after a restart they hold zero until the
410  // first advance -- but the coupler packs them for the ocean *before*
411  // that advance, which delivers zero wind stress and zero heat flux for a
412  // whole ocean step with no diagnostic. They are part of the coupled
413  // interface and have to survive the checkpoint.
414  //
415  // Written only when a coupling driver is attached, so standalone
416  // checkpoints are byte-for-byte unchanged. Each field is written
417  // separately because diffusion and moisture settings decide which exist;
418  // the read side probes for each rather than assuming.
420  if (!Tau.empty() && Tau[lev][TauType::tau13]) {
421  MultiFab tau13(convert(grids[lev],IntVect(1,0,1)),dmap[lev],1,0);
422  MultiFab::Copy(tau13,*Tau[lev][TauType::tau13],0,0,1,0);
423  VisMF::Write(tau13, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Tau13"));
424  }
425  if (!Tau.empty() && Tau[lev][TauType::tau23]) {
426  MultiFab tau23(convert(grids[lev],IntVect(0,1,1)),dmap[lev],1,0);
427  MultiFab::Copy(tau23,*Tau[lev][TauType::tau23],0,0,1,0);
428  VisMF::Write(tau23, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Tau23"));
429  }
430  if (!SFS_hfx3_lev.empty() && SFS_hfx3_lev[lev]) {
431  MultiFab hfx3(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
432  MultiFab::Copy(hfx3,*SFS_hfx3_lev[lev],0,0,1,0);
433  VisMF::Write(hfx3, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "SFS_hfx3"));
434  }
435  if (!SFS_q1fx3_lev.empty() && SFS_q1fx3_lev[lev]) {
436  MultiFab q1fx3(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,0);
437  MultiFab::Copy(q1fx3,*SFS_q1fx3_lev[lev],0,0,1,0);
438  VisMF::Write(q1fx3, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "SFS_q1fx3"));
439  }
440  }
441 
442  // The running sum of the time-averaged velocity. Its normalizer, t_avg_cnt,
443  // goes in the header above; both are needed or the average silently restarts
444  // from zero across a checkpoint/restart (issue 3654).
445  // NOTE: no ghost cells to strip here, so this is written in place.
447  AMREX_ALWAYS_ASSERT(vel_t_avg[lev] != nullptr);
448  VisMF::Write(*vel_t_avg[lev], MultiFabFileFullPrefix(lev, checkpointname, "Level_", "VelTimeAvg"));
449  }
450 
451  // Face state of the immersed-boundary surface energy balance (no-op unless enabled);
452  // read back in init_ibseb() once the face list has been rebuilt from the blanking.
453  ibseb_write_checkpoint(checkpointname, lev);
454 
456  AMREX_ALWAYS_ASSERT(interval_means[lev] != nullptr);
457  VisMF::Write(*interval_means[lev],
458  MultiFabFileFullPrefix(lev, checkpointname, "Level_", "IntervalMeans"));
459  }
460 
461  // Note that we write the ghost cells of the base state (unlike above)
462  IntVect ng_base = base_state[lev].nGrowVect();
463  int ncomp_base = base_state[lev].nComp();
464  MultiFab base(grids[lev],dmap[lev],ncomp_base,ng_base);
465  MultiFab::Copy(base,base_state[lev],0,0,ncomp_base,ng_base);
466  VisMF::Write(base, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "BaseState"));
467 
468  if (SolverChoice::mesh_type != MeshType::ConstantDz) {
469  // Note that we also write the ghost cells of z_phys_nd
470  IntVect ng = z_phys_nd[lev]->nGrowVect();
471  MultiFab z_height(convert(grids[lev],IntVect(1,1,1)),dmap[lev],1,ng);
472  MultiFab::Copy(z_height,*z_phys_nd[lev],0,0,1,ng);
473  VisMF::Write(z_height, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Z_Phys_nd"));
474  }
475 
476  // We must read and write qmoist with ghost cells because we don't directly impose BCs on these vars
477  // Write the moisture model restart variables
478  std::vector<int> qmoist_indices;
479  std::vector<std::string> qmoist_names;
480  micro->Get_Qmoist_Restart_Vars(lev, solverChoice, qmoist_indices, qmoist_names);
481  int qmoist_nvar = static_cast<int>(qmoist_indices.size());
482  for (int var = 0; var < qmoist_nvar; var++) {
483  const int ncomp = 1;
484  IntVect ng_moist = qmoist[lev][qmoist_indices[var]]->nGrowVect();
485  MultiFab moist_vars(grids[lev],dmap[lev],ncomp,ng_moist);
486  MultiFab::Copy(moist_vars,*(qmoist[lev][qmoist_indices[var]]),0,0,ncomp,ng_moist);
487  VisMF::Write(moist_vars, amrex::MultiFabFileFullPrefix(lev, checkpointname, "Level_", qmoist_names[var]));
488  }
489 
490 #if defined(ERF_USE_WINDFARM)
491  if(solverChoice.windfarm_type == WindFarmType::Fitch or
492  solverChoice.windfarm_type == WindFarmType::EWP or
493  solverChoice.windfarm_type == WindFarmType::SimpleAD){
494  IntVect ng_turb = Nturb[lev].nGrowVect();
495  MultiFab mf_Nturb(grids[lev],dmap[lev],1,ng_turb);
496  MultiFab::Copy(mf_Nturb,Nturb[lev],0,0,1,ng_turb);
497  VisMF::Write(mf_Nturb, amrex::MultiFabFileFullPrefix(lev, checkpointname, "Level_", "NumTurb"));
498  }
499 #endif
500 
501  // Two-stream radiation: the force-restore surface state
502  if (solverChoice.rad_type == RadiationType::TwoStream) {
503  two_stream_rad.write_checkpoint(lev, checkpointname);
504  }
505 
506  // Write the LSM data
507  if (solverChoice.lsm_type != LandSurfaceType::None) {
508  for (int ivar(0); ivar<lsm_data[lev].size(); ++ivar) {
509  BoxArray ba = lsm_data[lev][ivar]->boxArray();
510  DistributionMapping dm = lsm_data[lev][ivar]->DistributionMap();
511  IntVect ng = lsm_data[lev][ivar]->nGrowVect();
512  int nvar = lsm_data[lev][ivar]->nComp();
513  MultiFab lsm_vars(ba,dm,nvar,ng);
514  MultiFab::Copy(lsm_vars,*(lsm_data[lev][ivar]),0,0,nvar,ng);
515  VisMF::Write(lsm_vars, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LsmData" + std::to_string(ivar)));
516  }
517  for (int iflux(0); iflux<lsm_flux[lev].size(); ++iflux) {
518  BoxArray ba = lsm_flux[lev][iflux]->boxArray();
519  DistributionMapping dm = lsm_flux[lev][iflux]->DistributionMap();
520  IntVect ng = lsm_flux[lev][iflux]->nGrowVect();
521  int nvar = lsm_flux[lev][iflux]->nComp();
522  MultiFab lsm_vars(ba,dm,nvar,ng);
523  MultiFab::Copy(lsm_vars,*(lsm_flux[lev][iflux]),0,0,nvar,ng);
524  VisMF::Write(lsm_vars, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LsmFlux" + std::to_string(iflux)));
525  }
526 
527  // Write the full LSM prognostic state (e.g. NoahMP soil/snow/canopy)
528  // to chk*/noahmp_restart/Level_<lev>.nc so a restart reproduces a
529  // cold-start trajectory bitwise (issue #3255). The LSM model writes
530  // its blocks collectively; no-op for models without such state.
531  // Create the subdir on rank 0 and barrier before the collective open
532  // so no rank races ahead of the directory existing.
533  std::string LsmRestartDir(checkpointname + "/noahmp_restart");
534  if (ParallelDescriptor::IOProcessor()) {
535  amrex::UtilCreateDirectory(LsmRestartDir, 0755);
536  }
537  ParallelDescriptor::Barrier();
538  lsm.Write_Lsm_Restart(lev, LsmRestartDir);
539  }
540 
541  // Write the radiation heating rates
542  if ((solverChoice.rad_type != RadiationType::None) && (qheating_rates[lev])) {
543  int nrad = qheating_rates[lev]->nComp();
544  MultiFab mf_rad(grids[lev],dmap[lev],nrad,0);
545  MultiFab::Copy(mf_rad,*qheating_rates[lev],0,0,nrad,0);
546  VisMF::Write(mf_rad, amrex::MultiFabFileFullPrefix(lev, checkpointname, "Level_", "Qrad"));
547  }
548 
549  IntVect ng = mapfac[lev][MapFacType::m_x]->nGrowVect();
550  MultiFab mf_m(ba2d[lev],dmap[lev],1,ng);
551  MultiFab::Copy(mf_m,*mapfac[lev][MapFacType::m_x],0,0,1,ng);
552  VisMF::Write(mf_m, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_mx"));
553 
554 #if 0
556  MultiFab::Copy(mf_m,*mapfac[lev][MapFacType::m_y],0,0,1,ng);
557  VisMF::Write(mf_m, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_my"));
558  }
559 #endif
560 
561  ng = mapfac[lev][MapFacType::u_x]->nGrowVect();
562  MultiFab mf_u(convert(ba2d[lev],IntVect(1,0,0)),dmap[lev],1,ng);
563  MultiFab::Copy(mf_u,*mapfac[lev][MapFacType::u_x],0,0,1,ng);
564  VisMF::Write(mf_u, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_ux"));
565 
566 #if 0
568  MultiFab::Copy(mf_u,*mapfac[lev][MapFacType::u_y],0,0,1,ng);
569  VisMF::Write(mf_u, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_uy"));
570  }
571 #endif
572 
573  ng = mapfac[lev][MapFacType::v_x]->nGrowVect();
574  MultiFab mf_v(convert(ba2d[lev],IntVect(0,1,0)),dmap[lev],1,ng);
575  MultiFab::Copy(mf_v,*mapfac[lev][MapFacType::v_x],0,0,1,ng);
576  VisMF::Write(mf_v, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_vx"));
577 
578 #if 0
580  MultiFab::Copy(mf_v,*mapfac[lev][MapFacType::v_y],0,0,1,ng);
581  VisMF::Write(mf_v, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MapFactor_vy"));
582  }
583 #endif
584 
585  // Count number of surface layer boundaries to determine face prefix
586  int n_faces = 0;
587  for (OrientationIter oit; oit; ++oit) {
588  Orientation ori = oit();
589  if (phys_bc_type[ori] == ERF_BC::surface_layer) {
590  n_faces += 1;
591  }
592  }
593 
594  for (OrientationIter oit; oit; ++oit) {
595  Orientation ori = oit();
596  if (m_SurfaceLayer[ori]) {
597  amrex::Print() << "Writing SurfaceLayer variables at level " << lev << " for face " << ori << std::endl;
598  std::string face = "_" + std::to_string(ori);
599  // if we only use a single surface layer, don't add the face prefix
600  if (n_faces == 1 && static_cast<int>(ori) == Orientation::zlo()) {
601  face = "";
602  }
603 
604  // These MultiFabs live on a 2D BoxArray for planar terrain but on the full 3D
605  // BoxArray (with a z ghost cell) for EB terrain, so we write each one on its own
606  // BoxArray -- copying into a hard-wired 2D MultiFab would silently drop everything
607  // above k=0 for EB (issue #3560)
608  auto write_sl_var = [&] (MultiFab* src, const std::string& name) {
609  VisMF::Write(*src, MultiFabFileFullPrefix(lev, checkpointname, "Level_", name));
610  };
611 
612  write_sl_var(m_SurfaceLayer[ori]->get_u_star(lev), "Ustar" + face);
613  write_sl_var(m_SurfaceLayer[ori]->get_w_star(lev), "Wstar" + face);
614  write_sl_var(m_SurfaceLayer[ori]->get_t_star(lev), "Tstar" + face);
615  write_sl_var(m_SurfaceLayer[ori]->get_q_star(lev), "Qstar" + face);
616  write_sl_var(m_SurfaceLayer[ori]->get_olen(lev) , "Olen" + face);
617  write_sl_var(m_SurfaceLayer[ori]->get_q_surf(lev), "Qsurf" + face);
618  write_sl_var(m_SurfaceLayer[ori]->get_t_surf(lev), "Tsurf" + face);
619  write_sl_var(m_SurfaceLayer[ori]->get_pblh(lev) , "PBLH" + face);
620  write_sl_var(m_SurfaceLayer[ori]->get_z0(lev) , "Z0" + face);
621  // The exponentially filtered averages behind erf.most.time_average. The
622  // region policy carries the filter state in these MultiFabs (the plane and
623  // EB policies carry it in the plane averages written to the
624  // most_time_average file above), so without them the filter history is lost
625  // across a restart and the first update falls back to the instantaneous
626  // average.
627  //
628  // NOTE: we only write these once the filter has been initialized; before
629  // that they hold bogus_large_value, which must never be blended in.
630  if (m_SurfaceLayer[ori]->mac_avg_is_initialized(lev)) {
631  for (int iavg(0); iavg < m_SurfaceLayer[ori]->get_num_mac_avg(); ++iavg) {
632  write_sl_var(m_SurfaceLayer[ori]->get_mac_avg_ptr(lev,iavg),
633  "MOSTAvg" + std::to_string(iavg) + face);
634  }
635  }
636  }
637  }
638 
639  if (sst_lev[lev][0]) {
640  int ntimes = 1;
641  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
642  MultiFab sst_at_t(ba2d[lev],dmap[lev],1,ng);
643  for (int nt(0); nt<ntimes; ++nt) {
644  MultiFab::Copy(sst_at_t,*sst_lev[lev][nt],0,0,1,ng);
645  VisMF::Write(sst_at_t, MultiFabFileFullPrefix(lev, checkpointname, "Level_",
646  "SST_" + std::to_string(nt)));
647  }
648  }
649 
650  if (tsk_lev[lev][0]) {
651  int ntimes = 1;
652  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
653  MultiFab tsk_at_t(ba2d[lev],dmap[lev],1,ng);
654  for (int nt(0); nt<ntimes; ++nt) {
655  MultiFab::Copy(tsk_at_t,*tsk_lev[lev][nt],0,0,1,ng);
656  VisMF::Write(tsk_at_t, MultiFabFileFullPrefix(lev, checkpointname, "Level_",
657  "TSK_" + std::to_string(nt)));
658  }
659  }
660 
661  {
662  int ntimes = 1;
663  ng = vars_new[lev][Vars::cons].nGrowVect(); ng[2]=0;
664  MultiFab lmask_at_t(ba2d[lev],dmap[lev],1,ng);
665  for (int nt(0); nt<ntimes; ++nt) {
666  for (MFIter mfi(lmask_at_t); mfi.isValid(); ++mfi) {
667  const Box& bx = mfi.growntilebox();
668  Array4<int> const& src_arr = lmask_lev[lev][nt]->array(mfi);
669  Array4<Real> const& dst_arr = lmask_at_t.array(mfi);
670  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
671  {
672  dst_arr(i,j,k) = Real(src_arr(i,j,k));
673  });
674  }
675  VisMF::Write(lmask_at_t, MultiFabFileFullPrefix(lev, checkpointname, "Level_",
676  "LMASK_" + std::to_string(nt)));
677  }
678  }
679 
680  IntVect ngv = ng; ngv[2] = 0;
681 
682  // Write lat/lon if it exists
683  if (lat_m[lev] && lon_m[lev]) {
684  amrex::Print() << "Writing Lat/Lon variables at level " << lev << std::endl;
685  MultiFab lat(ba2d[lev],dmap[lev],1,ngv);
686  MultiFab lon(ba2d[lev],dmap[lev],1,ngv);
687  MultiFab::Copy(lat,*lat_m[lev],0,0,1,ngv);
688  MultiFab::Copy(lon,*lon_m[lev],0,0,1,ngv);
689  VisMF::Write(lat, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LAT"));
690  VisMF::Write(lon, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "LON"));
691  }
692 
693 
694 #ifdef ERF_USE_NETCDF
695  // Write sinPhi and cosPhi if it exists
696  if (cosPhi_m[lev] && sinPhi_m[lev] && solverChoice.variable_coriolis) {
697  amrex::Print() << "Writing Coriolis factors at level " << lev << std::endl;
698  MultiFab sphi(ba2d[lev],dmap[lev],1,ngv);
699  MultiFab cphi(ba2d[lev],dmap[lev],1,ngv);
700  MultiFab::Copy(sphi,*sinPhi_m[lev],0,0,1,ngv);
701  MultiFab::Copy(cphi,*cosPhi_m[lev],0,0,1,ngv);
702  VisMF::Write(sphi, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "SinPhi"));
703  VisMF::Write(cphi, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "CosPhi"));
704  }
705 
706  if (solverChoice.use_real_bcs && solverChoice.init_type == InitType::WRFInput) {
707  if (lev == 0) {
708  amrex::Print() << "Writing C1H/C2H/RDNW/MUB/PHB variables at level " << lev << std::endl;
709  MultiFab tmp1d(ba1d[0],dmap[0],1,0);
710 
711  MultiFab::Copy(tmp1d,*wrf_C1H,0,0,1,0);
712  VisMF::Write(tmp1d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "C1H"));
713 
714  MultiFab::Copy(tmp1d,*wrf_C2H,0,0,1,0);
715  VisMF::Write(tmp1d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "C2H"));
716 
717  MultiFab::Copy(tmp1d,*wrf_RDNW,0,0,1,0);
718  VisMF::Write(tmp1d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "RDNW"));
719 
720  MultiFab tmp2d(ba2d[0],dmap[0],1,wrf_MUB->nGrowVect());
721 
722  MultiFab::Copy(tmp2d,*wrf_MUB,0,0,1,wrf_MUB->nGrowVect());
723  VisMF::Write(tmp2d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "MUB"));
724 
725  MultiFab tmp3d(convert(grids[0],IntVect(0,0,1)),dmap[0],1,wrf_PHB->nGrowVect());
726 
727  MultiFab::Copy(tmp3d,*wrf_PHB,0,0,1,wrf_PHB->nGrowVect());
728  VisMF::Write(tmp3d, MultiFabFileFullPrefix(lev, checkpointname, "Level_", "PHB"));
729  }
730  }
731 #endif
732  } // for lev
733 
734  // Write zlevels to its own directory and read it as well, similar to bdy data
735  if (ParallelDescriptor::IOProcessor()) {
736  std::string ZLevelsFileName(checkpointname + "/zlevels");
737  std::ofstream ZLevelsFile;
738  ZLevelsFile.open(ZLevelsFileName.c_str(), std::ofstream::out |
739  std::ofstream::trunc |
740  std::ofstream::binary);
741  if(! ZLevelsFile.good()) {
742  FileOpenFailed(ZLevelsFileName);
743  } else {
744  ZLevelsFile.precision(17);
745 
746  // Write every level we hold (max_level+1) rather than finest_level+1, so
747  // that levels which are not currently active are still restored.
748  ZLevelsFile << zlevels_stag.size() << "\n";
749 
750  for (int lev = 0; lev < static_cast<int>(zlevels_stag.size()); ++lev) {
751  ZLevelsFile << zlevels_stag[lev].size();
752  for (int k = 0; k < static_cast<int>(zlevels_stag[lev].size()); ++k) {
753  ZLevelsFile << " " << zlevels_stag[lev][k];
754  }
755  ZLevelsFile << "\n";
756  }
757  }
758  }
759 
760 #ifdef ERF_USE_PARTICLES
761  particleData.Checkpoint(checkpointname);
762 #endif
763 
764 #if 0
765 #ifdef ERF_USE_NETCDF
766  // Write bdy_data files
767  if ( ParallelDescriptor::IOProcessor() &&
768  ((solverChoice.init_type==InitType::WRFInput) || (solverChoice.init_type==InitType::Metgrid)) &&
770  {
771  // Vector dimensions
772  int num_time = bdy_data_xlo.size();
773  int num_var = bdy_data_xlo[0].size();
774 
775  // Open header file and write to it
776  std::ofstream bdy_h_file(MultiFabFileFullPrefix(0, checkpointname, "Level_", "bdy_H"));
777  bdy_h_file << std::setprecision(1) << std::fixed;
778  bdy_h_file << num_time << "\n";
779  bdy_h_file << num_var << "\n";
780  bdy_h_file << start_bdy_time << "\n";
781  bdy_h_file << bdy_time_interval << "\n";
782  bdy_h_file << real_width << "\n";
783  for (int ivar(0); ivar<num_var; ++ivar) {
784  bdy_h_file << bdy_data_xlo[0][ivar].box() << "\n";
785  bdy_h_file << bdy_data_xhi[0][ivar].box() << "\n";
786  bdy_h_file << bdy_data_ylo[0][ivar].box() << "\n";
787  bdy_h_file << bdy_data_yhi[0][ivar].box() << "\n";
788  }
789 
790  // Open data file and write to it
791  std::ofstream bdy_d_file(MultiFabFileFullPrefix(0, checkpointname, "Level_", "bdy_D"));
792  for (int itime(0); itime<num_time; ++itime) {
793  if (bdy_data_xlo[itime].size() > 0) {
794  for (int ivar(0); ivar<num_var; ++ivar) {
795  bdy_data_xlo[itime][ivar].writeOn(bdy_d_file,0,1);
796  bdy_data_xhi[itime][ivar].writeOn(bdy_d_file,0,1);
797  bdy_data_ylo[itime][ivar].writeOn(bdy_d_file,0,1);
798  bdy_data_yhi[itime][ivar].writeOn(bdy_d_file,0,1);
799  }
800  }
801  }
802  }
803 #endif
804 #endif
805 
806  // Write job_info after checkpoint state so the provenance record describes
807  // the completed output attempt and can seed a later restart lineage.
809  istep[0], t_new[0]);
810 
811  if (verbose > 0)
812  {
813  auto dCheckTime = amrex::second() - dCheckTime0;
814  ParallelDescriptor::ReduceRealMax(dCheckTime,ParallelDescriptor::IOProcessorNumber());
815  amrex::Print() << "Checkpoint write time = " << dCheckTime << " seconds." << '\n';
816  }
817 }
void ibseb_write_checkpoint(const std::string &checkpointname, int lev) const
Definition: ERF_IBSEB.cpp:195
int Get_LSM_Step(const int &lev) const
Definition: ERF_LandSurface.H:196
void Write_Lsm_Restart(const int &lev, const std::string &dir) const
Definition: ERF_LandSurface.H:205
void write_checkpoint(int lev, const std::string &checkpointname) const
Write the prognostic surface state of one level (SEB on).
Definition: ERF_TwoStreamRadiation.cpp:263
bool variable_coriolis
Whether spatially varying Coriolis forcing is enabled.
Definition: ERF_DataStruct.H:2258
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,
double  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
2517 {
2518  AMREX_ALWAYS_ASSERT(nlevels <= bArray.size());
2519  AMREX_ALWAYS_ASSERT(nlevels <= my_ref_ratio.size()+1);
2520  AMREX_ALWAYS_ASSERT(nlevels <= level_steps.size());
2521 
2522  HeaderFile.precision(17);
2523 
2524  // ---- this is the generic plot file type name
2525  HeaderFile << versionName << '\n';
2526 
2527  HeaderFile << varnames.size() << '\n';
2528 
2529  for (int ivar = 0; ivar < varnames.size(); ++ivar) {
2530  HeaderFile << varnames[ivar] << "\n";
2531  }
2532  HeaderFile << AMREX_SPACEDIM << '\n';
2533  HeaderFile << my_time << '\n';
2534  HeaderFile << finest_level << '\n';
2535  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
2536  HeaderFile << my_geom[0].ProbLo(i) << ' ';
2537  }
2538  HeaderFile << '\n';
2539  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
2540  HeaderFile << my_geom[0].ProbHi(i) << ' ';
2541  }
2542  HeaderFile << '\n';
2543  for (int i = 0; i < finest_level; ++i) {
2544  HeaderFile << my_ref_ratio[i][0] << ' ';
2545  }
2546  HeaderFile << '\n';
2547  for (int i = 0; i <= finest_level; ++i) {
2548  HeaderFile << my_geom[i].Domain() << ' ';
2549  }
2550  HeaderFile << '\n';
2551  for (int i = 0; i <= finest_level; ++i) {
2552  HeaderFile << level_steps[i] << ' ';
2553  }
2554  HeaderFile << '\n';
2555  for (int i = 0; i <= finest_level; ++i) {
2556  for (int k = 0; k < AMREX_SPACEDIM; ++k) {
2557  HeaderFile << my_geom[i].CellSize()[k] << ' ';
2558  }
2559  HeaderFile << '\n';
2560  }
2561  HeaderFile << (int) my_geom[0].Coord() << '\n';
2562  HeaderFile << "0\n";
2563 
2564  for (int level = 0; level <= finest_level; ++level) {
2565  HeaderFile << level << ' ' << bArray[level].size() << ' ' << my_time << '\n';
2566  HeaderFile << level_steps[level] << '\n';
2567 
2568  const IntVect& domain_lo = my_geom[level].Domain().smallEnd();
2569  for (int i = 0; i < bArray[level].size(); ++i)
2570  {
2571  // Need to shift because the RealBox ctor we call takes the
2572  // physical location of index (0,0,0). This does not affect
2573  // the usual cases where the domain index starts with zero
2574  const Box& b = shift(bArray[level][i], -domain_lo);
2575  RealBox loc = RealBox(b, my_geom[level].CellSize(), my_geom[level].ProbLo());
2576  for (int n = 0; n < AMREX_SPACEDIM; ++n) {
2577  HeaderFile << loc.lo(n) << ' ' << loc.hi(n) << '\n';
2578  }
2579  }
2580 
2581  HeaderFile << MultiFabHeaderPath(level, levelPrefix, mfPrefix) << '\n';
2582  }
2583  HeaderFile << "1" << "\n";
2584  HeaderFile << "3" << "\n";
2585  HeaderFile << "amrexvec_nu_x" << "\n";
2586  HeaderFile << "amrexvec_nu_y" << "\n";
2587  HeaderFile << "amrexvec_nu_z" << "\n";
2588  std::string mf_nodal_prefix = "Nu_nd";
2589  for (int level = 0; level <= finest_level; ++level) {
2590  HeaderFile << MultiFabHeaderPath(level, levelPrefix, mf_nodal_prefix) << '\n';
2591  }
2592 }
Coord
Coordinate-axis selector.
Definition: ERF_DataStruct.H:170
Here is the call graph for this function:

◆ writeJobInfo()

void ERF::writeJobInfo ( const std::string &  dir,
erf_provenance::ArtifactType  artifact_type,
int  artifact_step,
double  artifact_time_seconds 
) const
19 {
20  if (!ParallelDescriptor::IOProcessor()) {
21  return;
22  }
23 
24  // job_info file with details about the run
25  std::ofstream jobInfoFile;
26  std::string FullPathJobInfoFile = dir;
27  FullPathJobInfoFile += "/job_info";
28  jobInfoFile.open(FullPathJobInfoFile.c_str(), std::ios::out | std::ios::trunc);
29  if (!jobInfoFile.good()) {
30  FileOpenFailed(FullPathJobInfoFile);
31  }
32 
34  provenance.execution = execution_provenance;
36  provenance.artifact.artifact_type = artifact_type;
37  provenance.artifact.artifact_step = artifact_step;
38  provenance.artifact.artifact_time_seconds = artifact_time_seconds;
40 
41  std::string PrettyLine = "==================================================="
42  "============================\n";
43  std::string OtherLine = "----------------------------------------------------"
44  "----------------------------\n";
45  std::string SkipSpace = " ";
46 
47  // job information
48  jobInfoFile << PrettyLine;
49  jobInfoFile << " ERF Job Information\n";
50  jobInfoFile << PrettyLine;
51 
52  jobInfoFile << erf_provenance::serialize_provenance_block(provenance);
53  jobInfoFile << "\n";
54 
55  jobInfoFile << "inputs file: " << inputs_name << "\n\n";
56 
57  jobInfoFile << "number of MPI processes: "
58  << ParallelDescriptor::NProcs() << "\n";
59 #ifdef _OPENMP
60  jobInfoFile << "number of threads: " << omp_get_max_threads() << "\n";
61 #endif
62 
63  jobInfoFile << "\n";
64  jobInfoFile << "CPU time used since start of simulation (CPU-hours): "
65  << getCPUTime() / Real(3600.0);
66 
67  jobInfoFile << "\n\n";
68 
69  if (use_datetime) {
70  const std::string dt_format = "%Y-%m-%d %H:%M:%S"; // ISO 8601 standard
71  jobInfoFile << "Simulation time: " << getTimestamp(start_time+t_new[0], dt_format) << "\n";
72  jobInfoFile << "\n\n";
73  }
74 
75  // Output information
76  jobInfoFile << PrettyLine;
77  jobInfoFile << " Output Information\n";
78  jobInfoFile << PrettyLine;
79 
80  jobInfoFile << "artifact type: "
81  << erf_provenance::artifact_type_token(artifact_type) << "\n";
82  jobInfoFile << "artifact step: " << artifact_step << "\n";
83  const auto existing_precision = jobInfoFile.precision();
84  jobInfoFile << std::setprecision(17)
85  << "simulation time: " << artifact_time_seconds << "\n";
86  jobInfoFile.precision(existing_precision);
87 
88  time_t now = time(nullptr);
89 
90  // Convert now to tm struct for local timezone
91  tm* localtm = localtime(&now);
92  jobInfoFile << "output data / time: " << asctime(localtm);
93 
94  std::string currentDir = FileSystem::CurrentPath();
95  jobInfoFile << "output dir: " << currentDir << "\n";
96 
97  jobInfoFile << "\n\n";
98 
99  // build information
100  jobInfoFile << PrettyLine;
101  jobInfoFile << " Build Information\n";
102  jobInfoFile << PrettyLine;
103 
104  jobInfoFile << "build date: " << buildInfoGetBuildDate() << "\n";
105  jobInfoFile << "build machine: " << buildInfoGetBuildMachine() << "\n";
106  jobInfoFile << "build dir: " << buildInfoGetBuildDir() << "\n";
107  jobInfoFile << "AMReX dir: " << buildInfoGetAMReXDir() << "\n";
108 
109  jobInfoFile << "\n";
110 
111  jobInfoFile << "COMP: " << buildInfoGetComp() << "\n";
112  jobInfoFile << "COMP version: " << buildInfoGetCompVersion() << "\n";
113 
114  jobInfoFile << "\n";
115 
116  for (int n = 1; n <= buildInfoGetNumModules(); n++) {
117  jobInfoFile << buildInfoGetModuleName(n) << ": "
118  << buildInfoGetModuleVal(n) << "\n";
119  }
120 
121  jobInfoFile << "\n";
122 
123  const char* githash1 = buildInfoGetGitHash(1);
124  const char* githash2 = buildInfoGetGitHash(2);
125  if (strlen(githash1) > 0) {
126  jobInfoFile << "ERF git hash: " << githash1 << "\n";
127  }
128  if (strlen(githash2) > 0) {
129  jobInfoFile << "AMReX git hash: " << githash2 << "\n";
130  }
131 
132  const char* buildgithash = buildInfoGetBuildGitHash();
133  const char* buildgitname = buildInfoGetBuildGitName();
134  if (strlen(buildgithash) > 0) {
135  jobInfoFile << buildgitname << " git hash: " << buildgithash << "\n";
136  }
137 
138  jobInfoFile << "\n\n";
139 
140  // grid information
141  jobInfoFile << PrettyLine;
142  jobInfoFile << " Grid Information\n";
143  jobInfoFile << PrettyLine;
144 
145  int f_lev = finest_level;
146 
147  for (int i = 0; i <= f_lev; i++) {
148  jobInfoFile << " level: " << i << "\n";
149  jobInfoFile << " number of boxes = " << grids[i].size() << "\n";
150  jobInfoFile << " maximum zones = ";
151  for (int n = 0; n < AMREX_SPACEDIM; n++) {
152  jobInfoFile << geom[i].Domain().length(n) << " ";
153  }
154  jobInfoFile << "\n\n";
155  }
156 
157  jobInfoFile << " Boundary conditions\n";
158 
159  jobInfoFile << " -x: " << domain_bc_type[0] << "\n";
160  jobInfoFile << " +x: " << domain_bc_type[3] << "\n";
161  jobInfoFile << " -y: " << domain_bc_type[1] << "\n";
162  jobInfoFile << " +y: " << domain_bc_type[4] << "\n";
163  jobInfoFile << " -z: " << domain_bc_type[2] << "\n";
164  jobInfoFile << " +z: " << domain_bc_type[5] << "\n";
165 
166  jobInfoFile << "\n\n";
167 
168  // runtime parameters
169  jobInfoFile << PrettyLine;
170  jobInfoFile << " Inputs File Parameters\n";
171  jobInfoFile << PrettyLine;
172 
173  ParmParse::dumpTable(jobInfoFile, true);
174  jobInfoFile.close();
175 }
std::string inputs_name
Definition: main.cpp:15
static double getCPUTime()
Definition: ERF.H:2029
std::string current_utc()
Definition: ERF_Provenance.cpp:318
std::string serialize_provenance_block(const ProvenanceRecord &record)
Definition: ERF_Provenance.cpp:347
std::string generate_uuid_v4()
Definition: ERF_Provenance.cpp:273
int artifact_step
Definition: ERF_Provenance.H:84
std::string artifact_uuid
Definition: ERF_Provenance.H:82
double artifact_time_seconds
Definition: ERF_Provenance.H:85
std::string artifact_created_utc
Definition: ERF_Provenance.H:86
ArtifactType artifact_type
Definition: ERF_Provenance.H:83
Definition: ERF_Provenance.H:90
ArtifactProvenance artifact
Definition: ERF_Provenance.H:92
ExecutionProvenance execution
Definition: ERF_Provenance.H:91
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 
)
644 {
645  std::ofstream ofs(filename);
646  if (!ofs.is_open()) {
647  amrex::Print() << "Error: Could not open file " << filename << " for writing.\n";
648  return;
649  }
650 
651  ofs << std::setprecision(10) << std::scientific;
652  ofs << "# x y\n";
653 
654  for (const auto& p : points_xy) {
655  ofs << p[0] << " " << p[1] << "\n";
656  }
657 
658  ofs.close();
659 
660  amrex::Print() << "Line plot data written to " << filename << "\n";
661 }

◆ 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,
double  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
2431 {
2432  BL_PROFILE("WriteMultiLevelPlotfileWithTerrain()");
2433 
2434  AMREX_ALWAYS_ASSERT(nlevels <= mf.size());
2435  AMREX_ALWAYS_ASSERT(nlevels <= rr.size()+1);
2436  AMREX_ALWAYS_ASSERT(nlevels <= level_steps.size());
2437  AMREX_ALWAYS_ASSERT(mf[0]->nComp() == varnames.size());
2438 
2439  bool callBarrier(false);
2440  PreBuildDirectorHierarchy(plotfilename, levelPrefix, nlevels, callBarrier);
2441  if (!extra_dirs.empty()) {
2442  for (const auto& d : extra_dirs) {
2443  const std::string ed = plotfilename+"/"+d;
2444  PreBuildDirectorHierarchy(ed, levelPrefix, nlevels, callBarrier);
2445  }
2446  }
2447  ParallelDescriptor::Barrier();
2448 
2449  if (ParallelDescriptor::MyProc() == ParallelDescriptor::NProcs()-1) {
2450  Vector<BoxArray> boxArrays(nlevels);
2451  for(int level(0); level < boxArrays.size(); ++level) {
2452  boxArrays[level] = mf[level]->boxArray();
2453  }
2454 
2455  auto f = [=,this]() {
2456  VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
2457  std::string HeaderFileName(plotfilename + "/Header");
2458  std::ofstream HeaderFile;
2459  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
2460  HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
2461  std::ofstream::trunc |
2462  std::ofstream::binary);
2463  if( ! HeaderFile.good()) FileOpenFailed(HeaderFileName);
2464  WriteGenericPlotfileHeaderWithTerrain(HeaderFile, nlevels, boxArrays, varnames,
2465  my_geom, time, level_steps, rr, versionName,
2466  levelPrefix, mfPrefix);
2467  };
2468 
2469  if (AsyncOut::UseAsyncOut()) {
2470  AsyncOut::Submit(std::move(f));
2471  } else {
2472  f();
2473  }
2474  }
2475 
2476  std::string mf_nodal_prefix = "Nu_nd";
2477  for (int level = 0; level <= finest_level; ++level)
2478  {
2479  if (AsyncOut::UseAsyncOut()) {
2480  VisMF::AsyncWrite(*mf[level],
2481  MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mfPrefix),
2482  true);
2483  VisMF::AsyncWrite(*mf_nd[level],
2484  MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mf_nodal_prefix),
2485  true);
2486  } else {
2487  const MultiFab* data;
2488  std::unique_ptr<MultiFab> mf_tmp;
2489  if (mf[level]->nGrowVect() != 0) {
2490  mf_tmp = std::make_unique<MultiFab>(mf[level]->boxArray(),
2491  mf[level]->DistributionMap(),
2492  mf[level]->nComp(), 0, MFInfo(),
2493  mf[level]->Factory());
2494  MultiFab::Copy(*mf_tmp, *mf[level], 0, 0, mf[level]->nComp(), 0);
2495  data = mf_tmp.get();
2496  } else {
2497  data = mf[level];
2498  }
2499  VisMF::Write(*data , MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mfPrefix));
2500  VisMF::Write(*mf_nd[level], MultiFabFileFullPrefix(level, plotfilename, levelPrefix, mf_nodal_prefix));
2501  }
2502  }
2503 }
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, double 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:2506
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 double  plot_per,
const double  dt_0,
double &  last_file_time 
)
3601 {
3602  bool write_now = false;
3603 
3604  if ( plot_int > 0) {
3605 
3606  write_now = (nstep % plot_int == 0);
3607 
3608  } else if (plot_per > zero) {
3609 
3610  amrex::Print() << "CUR NEXT PER " << cur_time << " " << next_file_time << " " << plot_per << std::endl;
3611 
3612  // Only write now if nstep newly matches the number of elapsed periods
3613  write_now = (cur_time > (next_file_time - 0.1*dt_0));
3614  }
3615 
3616  return write_now;
3617 }

◆ WriteSubvolume()

void ERF::WriteSubvolume ( int  isub,
amrex::Vector< std::string >  subvol_var_names 
)
114 {
115  ParmParse pp("erf.subvol");
116 
117  Vector<Real> origin;
118  Vector< int> ncell;
119  Vector<Real> delta;
120 
121  // **************************************************************
122  // Read in the origin, number of cells in each dir, and resolution
123  // **************************************************************
124 
125  int lev_for_sub = 0;
126  int offset = isub * AMREX_SPACEDIM;
127 
128  pp.getarr("origin",origin,offset,AMREX_SPACEDIM);
129  pp.getarr("nxnynz", ncell,offset,AMREX_SPACEDIM);
130  pp.getarr("dxdydz", delta,offset,AMREX_SPACEDIM);
131 
132  bool found = false;
133  for (int i = 0; i <= finest_level; i++) {
134  if (!found) {
135  if (almostEqual(delta[offset+0],geom[i].CellSize(0)) &&
136  almostEqual(delta[offset+1],geom[i].CellSize(1)) &&
137  almostEqual(delta[offset+2],geom[i].CellSize(2)) ) {
138 
139  amrex::Print() << "WriteSubvolume:Resolution specified matches that of level " << i << std::endl;
140  found = true;
141  lev_for_sub = i;
142  }
143  }
144  }
145 
146  if (!found) {
147  amrex::Abort("Resolution specified for subvol does not match the resolution of any of the levels.");
148  }
149 
150 
151  // **************************************************************
152  // Now that we know which level we're at, we can figure out which (i,j,k) the origin corresponds to
153  // Note we use Real(1.0001) as a fudge factor since the division of two reals --> integer will do a floor
154  // **************************************************************
155  int i0 = static_cast<int>((origin[offset+0] - geom[lev_for_sub].ProbLo(0)) * Real(1.0001) / delta[offset+0]);
156  int j0 = static_cast<int>((origin[offset+1] - geom[lev_for_sub].ProbLo(1)) * Real(1.0001) / delta[offset+1]);
157  int k0 = static_cast<int>((origin[offset+2] - geom[lev_for_sub].ProbLo(2)) * Real(1.0001) / delta[offset+2]);
158 
159  found = false;
160  if (almostEqual(geom[lev_for_sub].ProbLo(0)+i0*delta[offset+0],origin[offset+0]) &&
161  almostEqual(geom[lev_for_sub].ProbLo(1)+j0*delta[offset+1],origin[offset+1]) &&
162  almostEqual(geom[lev_for_sub].ProbLo(2)+k0*delta[offset+2],origin[offset+2]) )
163  {
164  amrex::Print() << "WriteSubvolume:Specified origin is the lower left corner of cell " << IntVect(i0,j0,k0) << std::endl;
165  found = true;
166  }
167 
168  if (!found) {
169  amrex::Abort("Origin specified does not correspond to a node at this level.");
170  }
171 
172  Box domain(geom[lev_for_sub].Domain());
173 
174  Box bx(IntVect(i0,j0,k0),IntVect(i0+ncell[offset+0]-1,j0+ncell[offset+1]-1,k0+ncell[offset+2]-1));
175  amrex::Print() << "WriteSubvolume:Box requested is " << bx << std::endl;
176 
177  if (!domain.contains(bx))
178  {
179  amrex::Abort("WriteSubvolume:Box requested is larger than the existing domain");
180  }
181 
182  Vector<int> cs(AMREX_SPACEDIM);
183  int count = pp.countval("chunk_size");
184  if (count > 0) {
185  pp.queryarr("chunk_size",cs,0,AMREX_SPACEDIM);
186  } else {
187  cs[0] = max_grid_size[0][0];
188  cs[1] = max_grid_size[0][1];
189  cs[2] = max_grid_size[0][2];
190  }
191  IntVect chunk_size(cs[0],cs[1],cs[2]);
192 
193  BoxArray ba(bx);
194  ba.maxSize(chunk_size);
195 
196  amrex::Print() << "WriteSubvolume:BoxArray is " << ba << std::endl;
197 
198  Vector<std::string> varnames;
199  varnames.insert(varnames.end(), subvol_var_names.begin(), subvol_var_names.end());
200 
201  int ncomp_mf = subvol_var_names.size();
202 
203  DistributionMapping dm(ba);
204 
205  MultiFab mf(ba, dm, ncomp_mf, 0);
206 
207  int mf_comp = 0;
208 
209  // *****************************************************************************************
210 
211  // First, copy any of the conserved state variables into the output plotfile
212  for (int i = 0; i < cons_names.size(); ++i) {
213  if (containerHasElement(subvol_var_names, cons_names[i])) {
214  mf.ParallelCopy(vars_new[lev_for_sub][Vars::cons],i,mf_comp,1,1,0);
215  mf_comp++;
216  }
217  }
218 
219  // *****************************************************************************************
220 
221  if (containerHasElement(subvol_var_names, "x_velocity") ||
222  containerHasElement(subvol_var_names, "y_velocity") ||
223  containerHasElement(subvol_var_names, "z_velocity"))
224  {
225  MultiFab mf_cc_vel(grids[lev_for_sub], dmap[lev_for_sub], AMREX_SPACEDIM, 0);
226  average_face_to_cellcenter(mf_cc_vel,0,
227  Array<const MultiFab*,3>{&vars_new[lev_for_sub][Vars::xvel],
228  &vars_new[lev_for_sub][Vars::yvel],
229  &vars_new[lev_for_sub][Vars::zvel]});
230  if (containerHasElement(subvol_var_names, "x_velocity")) {
231  mf.ParallelCopy(mf_cc_vel,0,mf_comp,1,0,0);
232  mf_comp++;
233  }
234  if (containerHasElement(subvol_var_names, "y_velocity")) {
235  mf.ParallelCopy(mf_cc_vel,1,mf_comp,1,0,0);
236  mf_comp++;
237  }
238  if (containerHasElement(subvol_var_names, "z_velocity")) {
239  mf.ParallelCopy(mf_cc_vel,2,mf_comp,1,0,0);
240  mf_comp++;
241  }
242  }
243 
244  // *****************************************************************************************
245 
246  // Finally, check for any derived quantities and compute them, inserting
247  // them into our output multifab
248  auto calculate_derived = [&](const std::string& der_name,
249  MultiFab& src_mf,
250  decltype(derived::erf_dernull)& der_function)
251  {
252  if (containerHasElement(subvol_var_names, der_name)) {
253  MultiFab dmf(src_mf.boxArray(), src_mf.DistributionMap(), 1, 0);
254  //
255  // NOTE: we must not tile in z here because some of the derived quantities
256  // ("precipitable", "mucape") are whole-column operations and require
257  // the full column in each box
258  //
259 #ifdef _OPENMP
260 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
261 #endif
262  for (MFIter mfi(dmf, TileNoZ()); mfi.isValid(); ++mfi)
263  {
264  const Box& tbx = mfi.tilebox();
265  auto& dfab = dmf[mfi];
266  auto& sfab = src_mf[mfi];
267  auto& zfab = (*z_phys_cc[lev_for_sub])[mfi];
268  der_function(tbx, dfab, 0, 1, sfab, zfab, Geom(lev_for_sub),
269  static_cast<Real>(t_new[0]), nullptr, lev_for_sub);
270  }
271  mf.ParallelCopy(dmf,0,mf_comp,1,0,0);
272  mf_comp++;
273  }
274  };
275 
276  // *****************************************************************************************
277  // NOTE: All derived variables computed below **MUST MATCH THE ORDER** of "derived_names"
278  // defined in ERF.H
279  // *****************************************************************************************
280 
281  // NOTE: the moisture map is the authority on what moist data exists, the same
282  // test setSubVolVariables used when it selected these names.
284 
285  if (mi.has_moisture()) {
286  calculate_derived("temp", vars_new[lev_for_sub][Vars::cons], derived::erf_dermoisttemp);
287  } else {
288  calculate_derived("temp", vars_new[lev_for_sub][Vars::cons], derived::erf_dertemp);
289  }
290  calculate_derived("theta", vars_new[lev_for_sub][Vars::cons], derived::erf_dertheta);
291  calculate_derived("KE", vars_new[lev_for_sub][Vars::cons], derived::erf_derKE);
292  calculate_derived("scalar", vars_new[lev_for_sub][Vars::cons], derived::erf_derscalar);
293  calculate_derived("soundspeed", vars_new[lev_for_sub][Vars::cons], derived::erf_dersoundspeed);
294  if (mi.has_moisture()) {
295  calculate_derived("precipitable", vars_new[lev_for_sub][Vars::cons], derived::erf_derprecipitable);
296  calculate_derived("mucape", vars_new[lev_for_sub][Vars::cons], derived::erf_dermucape);
297  }
298 
299  // *****************************************************************************************
300 
301  double time = t_new[lev_for_sub];
302 
303  std::string sf = subvol_file + "_" + std::to_string(isub);
304  std::string subvol_filename;
305 
307  const std::string dt_format = "%Y-%m-%d_%H:%M:%S"; // ISO 8601 standard
308  subvol_filename = sf + getTimestamp(start_time+time, dt_format);
309  } else {
310  subvol_filename = Concatenate(sf + "_", istep[0], file_name_digits);
311  }
312 
313  amrex::Print() <<"Writing subvolume into " << subvol_filename << std::endl;
314  WriteSingleLevelPlotfile(subvol_filename,mf,varnames,geom[lev_for_sub],static_cast<amrex::Real>(time),istep[0]);
315 
316 }
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 
)
99 {
100  std::ofstream vtkfile(filename);
101  if (!vtkfile.is_open()) {
102  std::cerr << "Error: Cannot open file " << filename << std::endl;
103  return;
104  }
105 
106  int num_points = points_xy.size();
107  if (num_points == 0) {
108  vtkfile << "# vtk DataFile Version three\n";
109  vtkfile << "Storm Track\n";
110  vtkfile << "ASCII\n";
111  vtkfile << "DATASET POLYDATA\n";
112  vtkfile << "POINTS " << num_points << " float\n";
113  vtkfile.close();
114  return;
115  }
116  if (num_points < 2) {
117  points_xy.push_back(points_xy[0]);
118  }
119  num_points = points_xy.size();
120 
121  vtkfile << "# vtk DataFile Version three\n";
122  vtkfile << "Storm Track\n";
123  vtkfile << "ASCII\n";
124  vtkfile << "DATASET POLYDATA\n";
125 
126  // Write points (Z=0 assumed)
127  vtkfile << "POINTS " << num_points << " float\n";
128  for (const auto& pt : points_xy) {
129  vtkfile << pt[0] << " " << pt[1] << " " << 10000.0 << std::endl;
130  }
131 
132  // Write polyline connectivity
133  vtkfile << "LINES 1 " << num_points + 1 << "\n";
134  vtkfile << num_points << " ";
135  for (int i = 0; i < num_points; ++i) {
136  vtkfile << i << " ";
137  }
138  vtkfile << "\n";
139 
140  vtkfile.close();
141 }

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

double ERF::bndry_output_planes_per = -1.0
staticprivate

◆ bndry_output_planes_start_time

double ERF::bndry_output_planes_start_time = 0.0
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

◆ cloud_chamber_budget

std::unique_ptr<CloudChamberBudget> ERF::cloud_chamber_budget
private

Referenced by ERF_shared().

◆ cloud_chamber_config

erf_cloud_chamber::Config ERF::cloud_chamber_config
private

Referenced by ERF_shared().

◆ column_file_name

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

◆ column_interval

int ERF::column_interval = -1
staticprivate

◆ column_kextent

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

Referenced by ERF_shared().

◆ column_kextent_hi_sentinel

constexpr int ERF::column_kextent_hi_sentinel = std::numeric_limits<int>::max()/2
staticconstexprprivate

◆ column_kextent_lo_sentinel

constexpr int ERF::column_kextent_lo_sentinel = std::numeric_limits<int>::min()/2
staticconstexprprivate

◆ column_loc_x

Real ERF::column_loc_x = zero
staticprivate

◆ column_loc_y

Real ERF::column_loc_y = zero
staticprivate

◆ column_per

double ERF::column_per = -1.0
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
private
Initial value:
{"soundspeed", "temp", "theta", "KE", "scalar",
"precipitable", "mucape"}

◆ 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<double> ERF::dt
private

Referenced by ERF_shared(), and EvolveOneStep().

◆ dt_max

double ERF::dt_max = 1.0e9
staticprivate

◆ dt_max_initial

double ERF::dt_max_initial = static_cast<double>(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

◆ execution_provenance

erf_provenance::ExecutionProvenance ERF::execution_provenance
private

Referenced by ERF_shared().

◆ 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<double> ERF::fixed_dt
private

◆ fixed_fast_dt

amrex::Vector<double> 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().

◆ ibseb_params

IBSEBParams ERF::ibseb_params
private

◆ 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

◆ interval_means

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

Referenced by ERF_shared().

◆ 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

double ERF::last_check_file_time = 0.0
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

double ERF::last_plot2d_file_time_1 = 0.0
staticprivate

◆ last_plot2d_file_time_2

double ERF::last_plot2d_file_time_2 = 0.0
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

double ERF::last_plot3d_file_time_1 = 0.0
staticprivate

◆ last_plot3d_file_time_2

double ERF::last_plot3d_file_time_2 = 0.0
staticprivate

◆ last_subvol_step

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

◆ last_subvol_time

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

◆ lat_m

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

◆ 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(), and GetLandMask().

◆ lon_m

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

◆ lsf

LargeScaleForcingData ERF::lsf
private

◆ lsf_data

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

Referenced by ERF_shared().

◆ lsm

LandSurface ERF::lsm
private

Referenced by ERF_shared().

◆ 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

double ERF::m_check_per = -1.0
private

◆ m_coupled_sst

std::unique_ptr<amrex::MultiFab> ERF::m_coupled_sst

Referenced by ApplyOceanSurfaceState().

◆ m_coupled_sst_valid

std::unique_ptr<amrex::iMultiFab> ERF::m_coupled_sst_valid

Referenced by ApplyOceanSurfaceState().

◆ m_driver_atm2ocn_uses_state_contract

bool ERF::m_driver_atm2ocn_uses_state_contract = true

◆ m_driver_has_atm2ocn_coupling

bool ERF::m_driver_has_atm2ocn_coupling = false

◆ m_driver_uses_two_way_coupling

bool ERF::m_driver_uses_two_way_coupling = false

◆ 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_ibseb

amrex::Vector<std::unique_ptr<IBFaceSet> > ERF::m_ibseb
private

◆ 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

double ERF::m_plot2d_per_1 = -1.0
private

◆ m_plot2d_per_2

double ERF::m_plot2d_per_2 = -1.0
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

double ERF::m_plot3d_per_1 = -1.0
private

◆ m_plot3d_per_2

double ERF::m_plot3d_per_2 = -1.0
private

◆ m_plot_face_terrain_blanking

bool ERF::m_plot_face_terrain_blanking = false
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<double> ERF::m_subvol_per
private

◆ m_SurfaceLayer

amrex::Vector<std::unique_ptr<SurfaceLayer> > ERF::m_SurfaceLayer
private

◆ m_w2d

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

◆ m_wall_scalar_bc_intent

amrex::Array<amrex::Array<erf_wall_scalar_bc::WallScalarBCIntent, AMREX_SPACEDIM*2>, NBCVAR_max> ERF::m_wall_scalar_bc_intent {}
private

◆ m_warned_coupled_sst_declined

bool ERF::m_warned_coupled_sst_declined = false

Referenced by ApplyOceanSurfaceState().

◆ 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

◆ mean_vars_time_reset_done

int ERF::mean_vars_time_reset_done {0}
private

Referenced by ERF_shared().

◆ 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().

◆ native_shoc_driver

amrex::Vector<std::unique_ptr<ShocDriver> > ERF::native_shoc_driver
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

◆ nudge_data

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

Referenced by ERF_shared().

◆ 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

◆ plot2d_var_names_2

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

◆ 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

double ERF::previousCPUTimeUsed = 0.0
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().

◆ r_plane_avg

amrex::Vector<amrex::TableData<amrex::Real, 1> > ERF::r_plane_avg
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

◆ rad_interp_from_coarse_pending

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

◆ real_extrap_w

bool ERF::real_extrap_w {true}
private

◆ real_width

int ERF::real_width {0}
private

◆ ref_tag_indicator_names

Vector< std::string > ERF::ref_tag_indicator_names
staticprivate

◆ 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

◆ 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

◆ 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

◆ sst_lev

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

Referenced by ERF_shared().

◆ start_time

double ERF::start_time = 0.0
staticprivate

Referenced by EvolveOneStep().

◆ startCPUTime

double ERF::startCPUTime = 0.0
staticprivate

Referenced by getCPUTime().

◆ station_sampler

std::unique_ptr<StationSampler> ERF::station_sampler = nullptr
private

◆ station_sampling_interval

int ERF::station_sampling_interval = -1
private

◆ station_sampling_per

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

◆ station_vars_2d

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

◆ station_vars_3d

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

◆ stop_time

double ERF::stop_time = std::numeric_limits<double>::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

Referenced by ERF_shared().

◆ subvol_file

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

◆ sum_interval

int ERF::sum_interval = -1
staticprivate

◆ sum_per

double ERF::sum_per = -1.0
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<double> ERF::t_avg_cnt
private

Referenced by ERF_shared().

◆ t_mean_cnt

amrex::Vector<double> ERF::t_mean_cnt
private

Referenced by ERF_shared().

◆ t_new

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

Referenced by ERF_shared(), and EvolveOneStep().

◆ t_old

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

Referenced by ERF_shared().

◆ t_plane_avg

amrex::Vector<amrex::TableData<amrex::Real, 1> > ERF::t_plane_avg
private

Referenced by ERF_shared().

◆ Tau

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

◆ 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().

◆ terrain_blanking_xface

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

Referenced by ERF_shared().

◆ terrain_blanking_yface

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

Referenced by ERF_shared().

◆ terrain_blanking_zface

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ERF::terrain_blanking_zface
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

◆ two_stream_rad

TwoStreamRadiation ERF::two_stream_rad
private

Referenced by ERF_shared().

◆ 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

◆ user_specified_inputs

std::set<std::string> ERF::user_specified_inputs

◆ 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

Referenced by PackAtmosphericStates().

◆ 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_bsp

BaseStateParams ERF::wrf_bsp
private

◆ 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

◆ wrf_RDNW

std::unique_ptr<amrex::MultiFab> ERF::wrf_RDNW
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: